"""
CNPERP subscription plan feature gates — standard, professional, enterprise.

Higher plans include all features from lower tiers.
"""
from __future__ import annotations

from typing import Any, Dict, FrozenSet, List, Optional, Tuple

from sqlalchemy.orm import Session

from app.core.config import settings

PLAN_STANDARD = "standard"
PLAN_PROFESSIONAL = "professional"
PLAN_ENTERPRISE = "enterprise"

PLAN_RANK: Dict[str, int] = {
    PLAN_STANDARD: 1,
    PLAN_PROFESSIONAL: 2,
    PLAN_ENTERPRISE: 3,
}

# Minimum plan required per feature id
FEATURE_MIN_PLAN: Dict[str, str] = {
    # ── Standard (core ERP) ─────────────────────────────────────────────────
    "core": PLAN_STANDARD,
    "pos": PLAN_STANDARD,
    "sales": PLAN_STANDARD,
    "purchases": PLAN_STANDARD,
    "inventory": PLAN_STANDARD,
    "accounting": PLAN_STANDARD,
    "banking": PLAN_STANDARD,
    "vat": PLAN_STANDARD,
    "reports_basic": PLAN_STANDARD,
    "settings": PLAN_STANDARD,
    # ── Professional ──────────────────────────────────────────────────────
    "manufacturing": PLAN_PROFESSIONAL,
    "job_cards": PLAN_PROFESSIONAL,
    "cogs": PLAN_PROFESSIONAL,
    "serial_numbers": PLAN_PROFESSIONAL,
    "procurement": PLAN_PROFESSIONAL,
    "budgeting": PLAN_PROFESSIONAL,
    "dimensional": PLAN_PROFESSIONAL,
    "billing": PLAN_PROFESSIONAL,
    "workflows": PLAN_PROFESSIONAL,
    "branch_sales": PLAN_PROFESSIONAL,
    "inventory_allocation": PLAN_PROFESSIONAL,
    "uom": PLAN_PROFESSIONAL,
    "landed_costs": PLAN_PROFESSIONAL,
    # ── Enterprise ──────────────────────────────────────────────────────────
    "bi": PLAN_ENTERPRISE,
    "traceability": PLAN_ENTERPRISE,
    "assets": PLAN_ENTERPRISE,
    "management_reports": PLAN_ENTERPRISE,
    "period_reports": PLAN_ENTERPRISE,
    "invoice_designer": PLAN_ENTERPRISE,
    "excel_templates": PLAN_ENTERPRISE,
    "logs": PLAN_ENTERPRISE,
    "cloud_sync": PLAN_ENTERPRISE,
    "backup_mgmt": PLAN_ENTERPRISE,
}

# Default caps when issuing a license (God Mode can override per key)
PLAN_DEFAULT_LIMITS: Dict[str, Dict[str, int]] = {
    PLAN_STANDARD: {"max_users": 10, "max_branches": 1},
    PLAN_PROFESSIONAL: {"max_users": 25, "max_branches": 5},
    PLAN_ENTERPRISE: {"max_users": 100, "max_branches": 25},
}

# API path prefix → feature id (first match wins)
API_PREFIX_FEATURES: Tuple[Tuple[str, str], ...] = (
    ("/api/v1/manufacturing", "manufacturing"),
    ("/api/v1/job-cards", "job_cards"),
    ("/api/v1/cogs", "cogs"),
    ("/api/v1/serial-numbers", "serial_numbers"),
    ("/api/v1/traceability", "traceability"),
    ("/api/v1/procurement", "procurement"),
    ("/api/v1/budgeting", "budgeting"),
    ("/api/v1/bi", "bi"),
    ("/api/v1/asset-management", "assets"),
    ("/api/v1/workflows", "workflows"),
    ("/api/v1/billing", "billing"),
    ("/api/v1/inventory-allocation", "inventory_allocation"),
    ("/api/v1/excel-templates", "excel_templates"),
    ("/api/v1/logs", "logs"),
    ("/api/v1/sync", "cloud_sync"),
    ("/api/v1/purchase-approvals", "workflows"),
    ("/api/v1/branch-sales", "branch_sales"),
    ("/api/v1/invoice-designer", "invoice_designer"),
)

# Static page path (no query) → feature id
STATIC_PAGE_FEATURES: Dict[str, str] = {
    "/static/manufacturing.html": "manufacturing",
    "/static/job-cards.html": "job_cards",
    "/static/job-sales-queue.html": "job_cards",
    "/static/recipes.html": "manufacturing",
    "/static/cogs.html": "cogs",
    "/static/cogs-reports.html": "cogs",
    "/static/serial-numbers.html": "serial_numbers",
    "/static/traceability.html": "traceability",
    "/static/procurement.html": "procurement",
    "/static/landed-costs.html": "landed_costs",
    "/static/budgeting.html": "budgeting",
    "/static/accounting-codes-dimensional.html": "dimensional",
    "/static/accounting-dimensions.html": "dimensional",
    "/static/dimensional-reports.html": "dimensional",
    "/static/billing.html": "billing",
    "/static/branch-sales.html": "branch_sales",
    "/static/inventory-allocation.html": "inventory_allocation",
    "/static/uom-management.html": "uom",
    "/static/business-intelligence.html": "bi",
    "/static/asset-management.html": "assets",
    "/static/asset.html": "assets",
    "/static/management-reports.html": "management_reports",
    "/static/period-reports.html": "period_reports",
    "/static/invoice-designer.html": "invoice_designer",
    "/static/invoice-customization.html": "invoice_designer",
    "/static/excel-templates.html": "excel_templates",
    "/static/logs-viewer.html": "logs",
    "/static/backup-management.html": "backup_mgmt",
}

PLAN_LABELS: Dict[str, str] = {
    PLAN_STANDARD: "Standard",
    PLAN_PROFESSIONAL: "Professional",
    PLAN_ENTERPRISE: "Enterprise",
}

PLAN_MARKETING: Dict[str, List[str]] = {
    PLAN_STANDARD: [
        "POS, sales, purchases & inventory",
        "Accounting, banking & VAT",
        "Standard financial & operational reports",
    ],
    PLAN_PROFESSIONAL: [
        "Everything in Standard",
        "Manufacturing, job cards & COGS",
        "Budgeting, procurement & workflows",
        "Dimensional accounting & branch sales",
    ],
    PLAN_ENTERPRISE: [
        "Everything in Professional",
        "Business intelligence & traceability",
        "Fixed assets & management reports",
        "Invoice designer, Excel templates & system logs",
        "Cloud sync hub integration",
    ],
}


def normalize_plan(plan: Optional[str]) -> str:
    p = (plan or PLAN_STANDARD).lower().strip()
    if p in (PLAN_PROFESSIONAL, "pro"):
        return PLAN_PROFESSIONAL
    if p in (PLAN_ENTERPRISE, "ent"):
        return PLAN_ENTERPRISE
    return PLAN_STANDARD


def plan_rank(plan: Optional[str]) -> int:
    return PLAN_RANK.get(normalize_plan(plan), 1)


def plan_includes(holder_plan: Optional[str], required_plan: str) -> bool:
    return plan_rank(holder_plan) >= plan_rank(required_plan)


def features_for_plan(plan: Optional[str]) -> List[str]:
    p = normalize_plan(plan)
    rank = plan_rank(p)
    return sorted(
        fid for fid, min_plan in FEATURE_MIN_PLAN.items()
        if PLAN_RANK.get(min_plan, 99) <= rank
    )


def feature_required_plan(feature_id: str) -> str:
    return FEATURE_MIN_PLAN.get(feature_id, PLAN_ENTERPRISE)


def company_has_feature(plan: Optional[str], feature_id: str) -> bool:
    required = FEATURE_MIN_PLAN.get(feature_id)
    if not required:
        return True
    return plan_includes(plan, required)


def feature_for_api_path(path: str) -> Optional[str]:
    for prefix, feature_id in API_PREFIX_FEATURES:
        if path.startswith(prefix):
            return feature_id
    return None


def feature_for_static_path(path: str) -> Optional[str]:
    base = path.split("?")[0].rstrip("/")
    if not base.endswith(".html"):
        return None
    if base in STATIC_PAGE_FEATURES:
        return STATIC_PAGE_FEATURES[base]
    # Prefix match for versioned static paths
    for static_path, fid in STATIC_PAGE_FEATURES.items():
        if base.endswith(static_path.replace("/static/", "/")):
            return fid
    return None


def resolve_company_plan(db: Session, company_id: str) -> str:
    from app.models.company import Company
    from app.models.licensing import LicenseKey

    company = db.query(Company).filter(Company.id == company_id).first()
    if not company:
        return PLAN_STANDARD
    if company.license_key_id:
        record = db.query(LicenseKey).filter(LicenseKey.id == company.license_key_id).first()
        if record and record.plan:
            return normalize_plan(record.plan)
    return normalize_plan(company.subscription_plan)


def plan_feature_payload(db: Session, company_id: str) -> Dict[str, Any]:
    from app.services.licensing_service import LicensingService

    plan = resolve_company_plan(db, company_id)
    lic = LicensingService(db).company_license_status(company_id)
    max_users = None
    max_branches = None
    if company_id:
        from app.models.company import Company
        from app.models.licensing import LicenseKey

        company = db.query(Company).filter(Company.id == company_id).first()
        if company and company.license_key_id:
            record = db.query(LicenseKey).filter(LicenseKey.id == company.license_key_id).first()
            if record:
                max_users = record.max_users
                max_branches = record.max_branches

    if not settings.license_enforcement:
        all_features = features_for_plan(PLAN_ENTERPRISE)
        return {
            "plan": plan,
            "plan_label": PLAN_LABELS.get(plan, plan.title()),
            "features": all_features,
            "enforcement": False,
            "licensed": True,
            "max_users": max_users,
            "max_branches": max_branches,
            "plan_tiers": _public_tier_summary(),
        }

    return {
        "plan": plan,
        "plan_label": PLAN_LABELS.get(plan, plan.title()),
        "features": features_for_plan(plan),
        "enforcement": True,
        "licensed": lic.get("licensed", False),
        "license_status": lic.get("status"),
        "max_users": max_users,
        "max_branches": max_branches,
        "plan_tiers": _public_tier_summary(),
    }


def assert_company_feature(db: Session, company_id: str, feature_id: str) -> None:
    if not settings.license_enforcement:
        return
    plan = resolve_company_plan(db, company_id)
    if not company_has_feature(plan, feature_id):
        required = feature_required_plan(feature_id)
        raise ValueError(
            f"This feature requires the {PLAN_LABELS.get(required, required)} plan or higher. "
            f"Your company is on {PLAN_LABELS.get(plan, plan)}. Contact CNP Solutions to upgrade."
        )


def assert_api_path_allowed(db: Session, company_id: str, path: str) -> Optional[str]:
    """Return error message if blocked, else None."""
    if not settings.license_enforcement:
        return None
    feature_id = feature_for_api_path(path)
    if not feature_id:
        return None
    plan = resolve_company_plan(db, company_id)
    if company_has_feature(plan, feature_id):
        return None
    required = feature_required_plan(feature_id)
    return (
        f"This API requires the {PLAN_LABELS.get(required, required)} plan or higher "
        f"(feature: {feature_id}). Contact CNP Solutions to upgrade."
    )


def locked_features_for_plan(plan: Optional[str]) -> List[Dict[str, str]]:
    p = normalize_plan(plan)
    allowed: FrozenSet[str] = frozenset(features_for_plan(p))
    locked = []
    for fid, min_plan in sorted(FEATURE_MIN_PLAN.items(), key=lambda x: PLAN_RANK.get(x[1], 0)):
        if fid not in allowed and fid != "core":
            locked.append({
                "id": fid,
                "required_plan": min_plan,
                "required_plan_label": PLAN_LABELS.get(min_plan, min_plan),
            })
    return locked


def _public_tier_summary() -> List[Dict[str, Any]]:
    return [
        {
            "id": pid,
            "label": PLAN_LABELS[pid],
            "highlights": PLAN_MARKETING[pid],
            "default_limits": PLAN_DEFAULT_LIMITS[pid],
        }
        for pid in (PLAN_STANDARD, PLAN_PROFESSIONAL, PLAN_ENTERPRISE)
    ]


def default_limits_for_plan(plan: Optional[str]) -> Dict[str, int]:
    return dict(PLAN_DEFAULT_LIMITS.get(normalize_plan(plan), PLAN_DEFAULT_LIMITS[PLAN_STANDARD]))
