"""Platform-wide operations across all tenants (CNP operator only)."""
from __future__ import annotations

from datetime import datetime
from pathlib import Path
from typing import Any, Dict, List, Optional

from sqlalchemy import func
from sqlalchemy.orm import Session

from app.core.config import settings
from app.core.security import create_access_token, get_password_hash
from app.models.branch import Branch
from app.models.company import Company
from app.models.licensing import DeploymentNode, LicenseKey, BranchLicense
from app.models.user import User
from app.services.licensing_service import LicensingService, decrypt_license_key_from_storage


def _read_version() -> Optional[str]:
    try:
        p = Path(__file__).resolve().parents[2] / "version.txt"
        return p.read_text(encoding="utf-8").strip()
    except OSError:
        return None


class PlatformAdminService:
    def __init__(self, db: Session):
        self.db = db

    def dashboard(self) -> Dict[str, Any]:
        companies_total = self.db.query(func.count(Company.id)).scalar() or 0
        companies_active = (
            self.db.query(func.count(Company.id)).filter(Company.is_active == True).scalar() or 0
        )
        users_total = self.db.query(func.count(User.id)).scalar() or 0
        users_active = (
            self.db.query(func.count(User.id)).filter(User.active == True).scalar() or 0
        )
        licenses_active = (
            self.db.query(func.count(LicenseKey.id)).filter(LicenseKey.status == "active").scalar() or 0
        )
        licenses_pending = (
            self.db.query(func.count(LicenseKey.id)).filter(LicenseKey.status == "pending").scalar() or 0
        )
        cutoff = datetime.utcnow()
        nodes_online = (
            self.db.query(func.count(DeploymentNode.id))
            .filter(DeploymentNode.is_active == True, DeploymentNode.last_heartbeat_at.isnot(None))
            .scalar()
            or 0
        )
        from app.services.session_tracking_service import SessionTrackingService

        active_sessions = SessionTrackingService(self.db).count_active()
        return {
            "companies_total": companies_total,
            "companies_active": companies_active,
            "users_total": users_total,
            "users_active": users_active,
            "licenses_active": licenses_active,
            "licenses_pending": licenses_pending,
            "deployment_nodes_online": nodes_online,
            "active_sessions": active_sessions,
            "app_version": _read_version(),
            "deployment_mode": settings.deployment_mode,
        }

    def list_companies(self, search: Optional[str] = None, active_only: Optional[bool] = None) -> List[Dict]:
        q = self.db.query(Company)
        if search:
            like = f"%{search}%"
            q = q.filter(
                (Company.name.ilike(like))
                | (Company.company_code.ilike(like))
                | (Company.email.ilike(like))
            )
        if active_only is True:
            q = q.filter(Company.is_active == True)
        elif active_only is False:
            q = q.filter(Company.is_active == False)
        companies = q.order_by(Company.created_at.desc()).limit(500).all()
        lic_svc = LicensingService(self.db)
        out = []
        for c in companies:
            user_count = (
                self.db.query(func.count(User.id)).filter(User.company_id == c.id).scalar() or 0
            )
            branch_count = (
                self.db.query(func.count(Branch.id)).filter(Branch.company_id == c.id).scalar() or 0
            )
            lic_status = lic_svc.company_license_status(c.id) if c.id else {}
            lk = None
            if c.license_key_id:
                lk = self.db.query(LicenseKey).filter(LicenseKey.id == c.license_key_id).first()
            if not lk:
                lk = (
                    self.db.query(LicenseKey)
                    .filter(
                        LicenseKey.company_id == c.id,
                        LicenseKey.status != "revoked",
                    )
                    .order_by(LicenseKey.created_at.desc())
                    .first()
                )
            prefix = lk.key_prefix if lk else None
            license_key = decrypt_license_key_from_storage(lk.key_encrypted) if lk else None
            branches = (
                self.db.query(Branch)
                .filter(Branch.company_id == c.id)
                .order_by(Branch.is_head_office.desc(), Branch.name)
                .all()
            )
            branch_rows = [
                {
                    "id": b.id,
                    "code": b.code,
                    "name": b.name,
                    "is_head_office": bool(b.is_head_office),
                    "active": bool(b.active),
                }
                for b in branches
            ]
            out.append({
                "id": c.id,
                "name": c.name,
                "company_code": c.company_code,
                "email": c.email,
                "is_active": bool(c.is_active),
                "is_setup_complete": bool(c.is_setup_complete),
                "deployment_mode": c.deployment_mode,
                "license_expires_at": c.license_expires_at or (lk.valid_until if lk else None),
                "license_status": lic_status.get("status"),
                "license_record_status": lk.status if lk else None,
                "license_key_id": lk.id if lk else c.license_key_id,
                "license_key_prefix": prefix,
                "license_key": license_key,
                "license_plan": lk.plan if lk else None,
                "license_max_branches": lk.max_branches if lk else None,
                "license_max_users": lk.max_users if lk else None,
                "has_license": lk is not None,
                "user_count": user_count,
                "branch_count": branch_count,
                "branches": branch_rows,
                "created_at": c.created_at,
            })
        return out

    def patch_company(self, company_id: str, data: Dict[str, Any]) -> Company:
        company = self.db.query(Company).filter(Company.id == company_id).first()
        if not company:
            raise ValueError("Company not found")
        if "is_active" in data and data["is_active"] is not None:
            company.is_active = data["is_active"]
        if data.get("subscription_plan"):
            company.subscription_plan = data["subscription_plan"]
        self.db.commit()
        self.db.refresh(company)
        return company

    def list_company_users(self, company_id: str) -> List[Dict]:
        users = (
            self.db.query(User)
            .filter(User.company_id == company_id)
            .order_by(User.username)
            .all()
        )
        out = []
        for u in users:
            branch_name = branch_code = None
            if u.branch_id:
                br = self.db.query(Branch).filter(Branch.id == u.branch_id).first()
                if br:
                    branch_name = br.name
                    branch_code = br.code
            out.append({
                "id": u.id,
                "username": u.username,
                "email": u.email,
                "role": u.role,
                "active": bool(u.active),
                "branch_id": u.branch_id,
                "branch_name": branch_name,
                "branch_code": branch_code,
                "last_login": u.last_login,
            })
        return out

    def reset_user_password(self, user_id: str, new_password: str) -> User:
        user = self.db.query(User).filter(User.id == user_id).first()
        if not user:
            raise ValueError("User not found")
        user.password_digest = get_password_hash(new_password)
        user.password_changed_at = datetime.utcnow()
        self.db.commit()
        self.db.refresh(user)
        return user

    def set_user_active(self, user_id: str, active: bool) -> User:
        user = self.db.query(User).filter(User.id == user_id).first()
        if not user:
            raise ValueError("User not found")
        user.active = active
        self.db.commit()
        self.db.refresh(user)
        return user

    def impersonate_user(self, user_id: str, base_url: str) -> Dict[str, Any]:
        user = self.db.query(User).filter(User.id == user_id).first()
        if not user:
            raise ValueError("User not found")
        if not user.active:
            raise ValueError("User is inactive")
        company = None
        if user.company_id:
            company = self.db.query(Company).filter(Company.id == user.company_id).first()
            if company and not company.is_active:
                raise ValueError("Company is deactivated")

        branch_code = None
        if user.branch_id:
            br = self.db.query(Branch).filter(Branch.id == user.branch_id).first()
            branch_code = br.code if br else None

        token = create_access_token(
            {
                "sub": str(user.id),
                "role": user.role,
                "username": user.username,
                "company_id": str(user.company_id) if user.company_id else None,
                "branch_id": str(user.branch_id) if user.branch_id else None,
                "branch_code": branch_code,
            },
            expires_delta=None,
        )
        base = (base_url or settings.license_cloud_url or "").rstrip("/")
        erp_url = f"{base}/static/index.html" if base else "/static/index.html"
        return {
            "access_token": token,
            "user_id": user.id,
            "username": user.username,
            "role": user.role,
            "company_id": user.company_id,
            "branch_id": user.branch_id,
            "branch_code": branch_code,
            "erp_login_url": erp_url,
        }

    def list_licenses(self, status_filter: Optional[str] = None) -> List[Dict]:
        q = self.db.query(LicenseKey).order_by(LicenseKey.created_at.desc())
        if status_filter:
            q = q.filter(LicenseKey.status == status_filter)
        rows = q.limit(300).all()
        out = []
        for k in rows:
            full = decrypt_license_key_from_storage(k.key_encrypted)
            out.append({
                "id": k.id,
                "key_prefix": k.key_prefix,
                "license_key": full,
                "key_stored": full is not None,
                "plan": k.plan,
                "status": k.status,
                "deployment_mode": k.deployment_mode,
                "valid_from": k.valid_from,
                "valid_until": k.valid_until,
                "company_id": k.company_id,
                "customer_name": k.customer_name,
                "customer_email": k.customer_email,
                "renewals_count": k.renewals_count or 0,
                "max_users": k.max_users or 25,
                "max_branches": k.max_branches or 1,
            })
        return out

    def _license_row_dict(self, k: LicenseKey) -> Dict:
        full = decrypt_license_key_from_storage(k.key_encrypted)
        return {
            "id": k.id,
            "key_prefix": k.key_prefix,
            "license_key": full,
            "key_stored": full is not None,
            "plan": k.plan,
            "status": k.status,
            "deployment_mode": k.deployment_mode,
            "valid_from": k.valid_from,
            "valid_until": k.valid_until,
            "company_id": k.company_id,
            "customer_name": k.customer_name,
            "customer_email": k.customer_email,
            "renewals_count": k.renewals_count or 0,
            "max_users": k.max_users or 25,
            "max_branches": k.max_branches or 1,
        }

    def get_company_licensing(self, company_id: str) -> Dict:
        company = self.db.query(Company).filter(Company.id == company_id).first()
        if not company:
            raise ValueError("Company not found")

        lic_svc = LicensingService(self.db)
        main = None
        if company.license_key_id:
            record = (
                self.db.query(LicenseKey).filter(LicenseKey.id == company.license_key_id).first()
            )
            if record:
                main = self._license_row_dict(record)

        branch_lics = (
            self.db.query(BranchLicense)
            .filter(BranchLicense.company_id == company_id)
            .order_by(BranchLicense.created_at.asc())
            .all()
        )
        bl_rows = []
        for bl in branch_lics:
            branch_name = branch_code = None
            if bl.branch_id:
                br = self.db.query(Branch).filter(Branch.id == bl.branch_id).first()
                if br:
                    branch_name = br.name
                    branch_code = br.code
            bl_rows.append({
                "id": bl.id,
                "slot_code": bl.slot_code,
                "label": bl.label,
                "status": bl.status,
                "valid_from": bl.valid_from,
                "valid_until": bl.valid_until,
                "branch_id": bl.branch_id,
                "branch_name": branch_name,
                "branch_code": branch_code,
                "notes": bl.notes,
            })

        branches = (
            self.db.query(Branch)
            .filter(Branch.company_id == company_id)
            .order_by(Branch.name)
            .all()
        )
        branch_opts = [
            {
                "id": b.id,
                "name": b.name,
                "code": b.code,
                "is_head_office": bool(b.is_head_office),
                "active": bool(b.active),
            }
            for b in branches
        ]

        used = lic_svc.count_company_branches(company_id)
        allowed = lic_svc.get_max_branches_for_company(company_id)

        return {
            "company_id": company.id,
            "company_name": company.name,
            "company_code": company.company_code,
            "branches_used": used,
            "branches_allowed": allowed,
            "main_includes_branches": LicensingService.MAIN_INCLUDES_BRANCHES,
            "has_main_license": main is not None,
            "main_license": main,
            "branch_licenses": bl_rows,
            "company_branches": branch_opts,
        }

    def assign_license_to_company(self, company_id: str, license_id: str) -> Dict:
        company = self.db.query(Company).filter(Company.id == company_id).first()
        if not company:
            raise ValueError("Company not found")
        record = self.db.query(LicenseKey).filter(LicenseKey.id == license_id).first()
        if not record:
            raise ValueError("License not found")
        if record.status == "revoked":
            raise ValueError("License is revoked")
        if record.company_id and record.company_id != company_id:
            raise ValueError("License is already assigned to another company")

        now = datetime.utcnow()
        record.company_id = company.id
        record.status = "active"
        record.activated_at = record.activated_at or now
        company.license_key_id = record.id
        company.license_expires_at = record.valid_until
        if record.customer_name and not company.name:
            pass  # keep company name
        self.db.commit()
        self.db.refresh(company)
        return self.get_company_licensing(company_id)

    def list_deployment_nodes(self) -> List[Dict]:
        nodes = (
            self.db.query(DeploymentNode)
            .order_by(DeploymentNode.last_heartbeat_at.desc().nullslast())
            .limit(200)
            .all()
        )
        out = []
        for n in nodes:
            lic = self.db.query(LicenseKey).filter(LicenseKey.id == n.license_key_id).first()
            company = None
            if n.company_id:
                company = self.db.query(Company).filter(Company.id == n.company_id).first()
            out.append({
                "id": n.id,
                "node_name": n.node_name,
                "deployment_type": n.deployment_type,
                "public_base_url": n.public_base_url,
                "is_active": n.is_active,
                "last_heartbeat_at": n.last_heartbeat_at,
                "last_ip": n.last_ip,
                "app_version": n.app_version,
                "company_id": n.company_id,
                "company_name": company.name if company else None,
                "license_prefix": lic.key_prefix if lic else None,
                "last_sync_at": n.last_sync_at,
                "last_sync_status": n.last_sync_status,
                "last_sync_events_count": n.last_sync_events_count or 0,
                "sync_requested_at": n.sync_requested_at,
            })
        return out

    def request_node_sync(self, node_id: str) -> Dict:
        from datetime import datetime

        node = self.db.query(DeploymentNode).filter(DeploymentNode.id == node_id).first()
        if not node:
            raise ValueError("Node not found")
        node.sync_requested_at = datetime.utcnow()
        self.db.flush()
        return {
            "node_id": node.id,
            "node_name": node.node_name,
            "message": "Sync requested — LAN server will push on next heartbeat or background cycle.",
        }
