from urllib.parse import parse_qsl, urlencode

from fastapi.templating import Jinja2Templates
from sqlalchemy import select

templates = Jinja2Templates(directory="app")

from app.core.db.common import CommonSessionLocal
from app.modules.core.iam.scope import build_scope, list_visible_branches, list_visible_tenants
from app.modules.core.rbac.ui_permissions import (
    can_change_branch_tenant,
    can_export_clients,
    can_import_service_tasks,
    can_import_services,
    can_manage_branches,
    can_manage_clients,
    can_manage_rbac,
    can_manage_service_tasks,
    can_manage_services,
    can_manage_settings,
    can_manage_tenants,
    can_manage_users,
    can_view_employee_dashboard,
    can_view_employees,
    can_manage_employees,
    can_change_employee_status,
    can_switch_employee_tenant,
    can_switch_employee_branch,
    can_view_employee_portal,
    can_edit_own_employee_profile,
    can_view_own_employee_work,
    can_manage_employee_work,
    can_view_employee_progress,
    can_request_employee_registration,
    can_approve_employee_registrations,
    can_punch_employee_attendance,
    can_view_own_employee_attendance,
    can_view_all_employee_attendance,
    can_approve_employee_attendance,
    can_apply_employee_leave,
    can_view_own_employee_leave,
    can_view_all_employee_leave,
    can_approve_employee_leave,
    can_manage_employee_leave_types,
    can_manage_employee_leave_balances,
    can_view_own_employee_documents,
    can_upload_own_employee_documents,
    can_view_all_employee_documents,
    can_manage_employee_documents,
    can_verify_employee_documents,
    can_manage_employee_document_types,
    can_view_employee_onboarding,
    can_manage_employee_onboarding,
    can_approve_employee_onboarding,
    can_view_employee_offboarding,
    can_manage_employee_offboarding,
    can_approve_employee_offboarding,
    can_request_own_employee_offboarding,
    can_import_employee_hr,
    can_manage_employee_payroll_structures,
    can_run_employee_payroll,
    can_view_employee_payroll,
    can_view_own_employee_payslips,
    can_approve_employee_payroll,
    can_view_consultants,
    can_manage_consultants,
    can_link_consultant_clients,
    can_manage_consultant_service_requests,
    can_manage_consultant_conversions,
    can_view_consultant_portal,
    can_manage_own_consultant_workspace,
    can_switch_client_branch,
    can_switch_client_tenant,
    can_switch_service_branch,
    can_switch_service_tenant,
    can_view_audit,
    can_view_branches,
    can_view_clients,
    can_view_billing,
    can_create_billing,
    can_generate_billing_invoices,
    can_view_billing_fee_structure,
    can_import_billing_fee_structure,
    can_view_platform_billing,
    can_manage_platform_billing,
    can_generate_platform_billing,
    can_manage_platform_plans,
    can_manage_platform_subscriptions,
    can_view_marketplace_leads,
    can_create_marketplace_leads,
    can_assign_marketplace_leads,
    can_update_marketplace_leads,
    can_convert_marketplace_leads,
    can_view_documents,
    can_upload_documents,
    can_download_documents,
    can_delete_documents,
    can_view_rbac,
    can_view_services,
    can_view_settings,
    can_view_tenants,
    can_view_users,
    can_view_own_alerts,
    can_manage_alerts,
    can_view_notice_cases,
    can_manage_notice_cases,
    can_upload_notice_case_documents,
    can_download_notice_case_documents,
    can_delete_notice_case_documents,
)


def build_page_url(base_url: str, page: int, query: str | None = None) -> str:
    params = dict(parse_qsl((query or "").lstrip("?"), keep_blank_values=True))
    params["page"] = str(page)
    qs = urlencode(params)
    return f"{base_url}?{qs}" if qs else base_url


def get_active_tenant_id(request, current_user=None):
    if not current_user:
        return None
    return request.session.get("active_tenant_id") or getattr(current_user, "tenant_id", None)


def get_active_branch_id(request, current_user=None):
    if not current_user:
        return None
    # return None when "all branches" context is active
    val = request.session.get("active_branch_id")
    if val in (None, "", 0, "0"):
        return None
    return val


def get_active_tenant_code(request, current_user=None):
    if not current_user:
        return None
    return request.session.get("active_tenant_code") or request.session.get("tenant_code") or getattr(request.state, "tenant_code", None)


def get_active_branch_code(request, current_user=None):
    if not current_user:
        return None
    val = request.session.get("active_branch_code") or request.session.get("branch_code")
    return val or getattr(request.state, "branch_code", None)


def get_active_financial_year(request, current_user=None):
    if not current_user:
        return None
    return request.session.get("active_financial_year") or getattr(request.state, "year_code", None)


def get_active_assessment_year(request, current_user=None):
    if not current_user:
        return None
    fy_code = get_active_financial_year(request, current_user)
    if not fy_code:
        return None
    db = CommonSessionLocal()
    try:
        from app.modules.core.tenancy.models import FinancialYear
        tenant_id = get_active_tenant_id(request, current_user) or getattr(current_user, "tenant_id", None)
        fy = db.execute(
            select(FinancialYear).where(
                FinancialYear.tenant_id == tenant_id,
                FinancialYear.year_code == fy_code,
            )
        ).scalar_one_or_none()
        return fy.assessment_year if fy else None
    except Exception:
        return None
    finally:
        db.close()


def get_unread_alert_count(request, current_user=None):
    if not current_user:
        return 0
    try:
        from app.modules.alerts.service import count_unread_alerts
    except Exception:
        return 0
    db = CommonSessionLocal()
    try:
        return count_unread_alerts(db, current_user)
    except Exception:
        return 0
    finally:
        db.close()



def _safe_static_path(path: str | None) -> str | None:
    path = (path or "").strip()
    if not path:
        return None
    if path.startswith("/static/"):
        return path
    if path.startswith("app/ui/static/"):
        return "/static/" + path.split("app/ui/static/", 1)[1]
    return path


def get_domain_context(request) -> dict:
    """Return safe domain context populated by Phase 7T.2 middleware."""
    try:
        ctx = getattr(request.state, "domain_context", None)
        return ctx if isinstance(ctx, dict) else {"is_resolved": False}
    except Exception:
        return {"is_resolved": False}


def _branding_default() -> dict:
    return {
        "firm_name": "Audit Firm ERP",
        "branch_name": "",
        "logo_url": None,
        "favicon_url": None,
        "primary_color": "#2563eb",
        "accent_color": "#0f172a",
        "contact_email": None,
        "contact_mobile": None,
        "website_url": None,
        "domain_name": None,
        "domain_type": None,
        "domain_resolved": False,
        "is_marketplace_domain": False,
        "is_consultant_domain": False,
        "consultant_name": None,
        "consultant_firm_name": None,
    }


def _tenant_branding_from_row(tenant, branch=None, default: dict | None = None) -> dict:
    default = default or _branding_default()
    if not tenant:
        return default.copy()
    return {
        **default,
        "firm_name": getattr(tenant, "display_name", None) or getattr(tenant, "name", None) or default["firm_name"],
        "branch_name": getattr(branch, "name", None) if branch else "All Branches",
        "logo_url": _safe_static_path(getattr(tenant, "logo_path", None)),
        "favicon_url": _safe_static_path(getattr(tenant, "favicon_path", None)),
        "primary_color": getattr(tenant, "primary_color", None) or default["primary_color"],
        "accent_color": getattr(tenant, "accent_color", None) or default["accent_color"],
        "contact_email": getattr(tenant, "contact_email", None),
        "contact_mobile": getattr(tenant, "contact_mobile", None),
        "website_url": getattr(tenant, "website_url", None),
    }


def _domain_branding(request, default: dict | None = None) -> dict:
    default = default or _branding_default()
    ctx = get_domain_context(request)
    if not ctx.get("is_resolved"):
        return default.copy()

    db = CommonSessionLocal()
    try:
        from app.modules.core.tenancy.models import Branch, Tenant
        from app.modules.consultants.models import ConsultantProfile
        from app.modules.core.iam.models import User
        domain_type = ctx.get("domain_type")
        tenant_id = ctx.get("tenant_id") or ctx.get("parent_tenant_id")
        branch_id = ctx.get("branch_id")
        consultant_id = ctx.get("consultant_id")

        tenant = db.execute(select(Tenant).where(Tenant.id == tenant_id)).scalar_one_or_none() if tenant_id else None
        branch = db.execute(select(Branch).where(Branch.id == branch_id)).scalar_one_or_none() if branch_id else None
        branding = _tenant_branding_from_row(tenant, branch, default)
        branding.update({
            "domain_name": ctx.get("domain_name") or ctx.get("host"),
            "domain_type": domain_type,
            "domain_resolved": True,
            "is_marketplace_domain": domain_type == "marketplace",
            "is_consultant_domain": str(domain_type or "").startswith("consultant_"),
        })

        if domain_type == "marketplace":
            branding["firm_name"] = "FilingABC"
            branding["branch_name"] = "Marketplace"
            return branding

        if consultant_id:
            consultant = db.execute(select(ConsultantProfile).where(ConsultantProfile.id == consultant_id)).scalar_one_or_none()
            if consultant:
                consultant_name = getattr(consultant, "contact_person", None) or getattr(consultant, "firm_name", None) or "Consultant"
                consultant_firm_name = getattr(consultant, "firm_name", None) or consultant_name
                branding["consultant_name"] = consultant_name
                branding["consultant_firm_name"] = consultant_firm_name
                branding["firm_name"] = consultant_firm_name
                branding["branch_name"] = "Consultant Workspace"
                branding["contact_email"] = getattr(consultant, "email", None) or branding.get("contact_email")
                branding["contact_mobile"] = getattr(consultant, "mobile", None) or branding.get("contact_mobile")

                # If the consultant user has a profile photo, use it as the domain logo.
                user_id = getattr(consultant, "user_id", None)
                if user_id:
                    user = db.execute(select(User).where(User.id == user_id)).scalar_one_or_none()
                    logo = _safe_static_path(getattr(user, "profile_photo_path", None))
                    if logo:
                        branding["logo_url"] = logo
        return branding
    except Exception:
        return default.copy()
    finally:
        db.close()


def get_current_tenant_name(request, current_user=None):
    if not current_user:
        return _domain_branding(request).get("firm_name") or "Audit Firm"
    db = CommonSessionLocal()
    try:
        from app.modules.core.tenancy.models import Tenant
        tenant_id = get_active_tenant_id(request, current_user)
        tenant = db.execute(select(Tenant).where(Tenant.id == tenant_id)).scalar_one_or_none()
        if not tenant:
            return _domain_branding(request).get("firm_name") or "Audit Firm"
        return getattr(tenant, "display_name", None) or tenant.name or "Audit Firm"
    except Exception:
        return _domain_branding(request).get("firm_name") or "Audit Firm"
    finally:
        db.close()


def get_current_branch_name(request, current_user=None):
    if not current_user:
        return _domain_branding(request).get("branch_name") or "-"
    db = CommonSessionLocal()
    try:
        from app.modules.core.tenancy.models import Branch
        branch_id = get_active_branch_id(request, current_user) or getattr(current_user, "branch_id", None)
        if not branch_id:
            return "All Branches"
        branch = db.execute(select(Branch).where(Branch.id == branch_id)).scalar_one_or_none()
        return branch.name if branch else "-"
    except Exception:
        return "-"
    finally:
        db.close()


def get_current_firm_branding(request, current_user=None):
    default = _branding_default()

    # Before login, domain branding is the only safe branding source. This supports
    # arrr.associates, auditfirm.filingabc.com, filingabc.com and consultant domains.
    if not current_user:
        return _domain_branding(request, default)

    db = CommonSessionLocal()
    try:
        from app.modules.core.tenancy.models import Branch, Tenant
        tenant_id = get_active_tenant_id(request, current_user)
        branch_id = get_active_branch_id(request, current_user) or getattr(current_user, "branch_id", None)
        tenant = db.execute(select(Tenant).where(Tenant.id == tenant_id)).scalar_one_or_none()
        branch = db.execute(select(Branch).where(Branch.id == branch_id)).scalar_one_or_none() if branch_id else None
        if not tenant:
            return _domain_branding(request, default)
        branding = _tenant_branding_from_row(tenant, branch, default)
        ctx = get_domain_context(request)
        if ctx.get("is_resolved"):
            branding.update({
                "domain_name": ctx.get("domain_name") or ctx.get("host"),
                "domain_type": ctx.get("domain_type"),
                "domain_resolved": True,
                "is_marketplace_domain": ctx.get("domain_type") == "marketplace",
                "is_consultant_domain": str(ctx.get("domain_type") or "").startswith("consultant_"),
            })
        return branding
    except Exception:
        return _domain_branding(request, default)
    finally:
        db.close()



def get_user_profile_photo_url(current_user=None):
    if not current_user:
        return None
    try:
        from app.modules.core.iam.profile_service import profile_photo_url
        return profile_photo_url(current_user)
    except Exception:
        return None


def get_user_initials(current_user=None):
    try:
        from app.modules.core.iam.profile_service import user_initials
        return user_initials(current_user)
    except Exception:
        return "U"


def get_client_sidebar_auditor_card(request, current_user=None):
    """Return the client-facing auditor card for the logged-in client user.

    This is used only by the sidebar. It reuses Phase 7Q.5 auditor_service and
    does not create or alter any business workflow.
    """
    if not current_user:
        return None
    db = CommonSessionLocal()
    try:
        from app.modules.clients.auditor_service import build_client_auditor_card
        from app.modules.clients.models import Client
        from app.modules.core.tenancy.models import Branch, Tenant

        tenant_id = get_active_tenant_id(request, current_user) or getattr(current_user, "tenant_id", None)
        email = (getattr(current_user, "email", None) or "").strip().lower()

        stmt = (
            select(Client, Tenant.name.label("tenant_name"), Branch.name.label("branch_name"))
            .join(Tenant, Tenant.id == Client.tenant_id, isouter=True)
            .join(Branch, Branch.id == Client.branch_id, isouter=True)
            .where(Client.is_active.is_(True), Client.is_archived.is_(False))
        )
        if tenant_id:
            stmt = stmt.where(Client.tenant_id == int(tenant_id))
        if email:
            stmt = stmt.where((Client.portal_user_id == current_user.id) | (Client.email == email) | (Client.alternate_email == email))
        else:
            stmt = stmt.where(Client.portal_user_id == current_user.id)

        result = db.execute(stmt.order_by(Client.id.desc())).first()
        if not result:
            return None

        client, tenant_name, branch_name = result
        client_row = {
            "id": client.id,
            "tenant_id": client.tenant_id,
            "branch_id": client.branch_id,
            "tenant_name": tenant_name,
            "branch_name": branch_name,
            "partner_id": client.partner_id,
            "default_review_partner_user_id": client.default_review_partner_user_id,
        }
        return build_client_auditor_card(db, client_row)
    except Exception:
        return None
    finally:
        db.close()

def get_context_tenants(request, current_user=None, permissions=None, role_names=None):
    if not current_user:
        return []

    if not (
        can_switch_service_tenant(current_user, permissions, role_names)
        or can_switch_client_tenant(current_user, permissions, role_names)
        or can_switch_employee_tenant(current_user, permissions, role_names)
    ):
        return []

    db = CommonSessionLocal()
    try:
        scope = build_scope(db, current_user)
        return list_visible_tenants(db, scope)
    finally:
        db.close()


def get_context_branches(request, current_user=None, permissions=None, role_names=None):
    if not current_user:
        return []

    if not (
        can_switch_service_branch(current_user, permissions, role_names)
        or can_switch_client_branch(current_user, permissions, role_names)
        or can_switch_employee_branch(current_user, permissions, role_names)
    ):
        return []

    db = CommonSessionLocal()
    try:
        scope = build_scope(db, current_user)
        tenant_id = int(get_active_tenant_id(request, current_user) or current_user.tenant_id)
        return list_visible_branches(db, scope, tenant_id=tenant_id)
    finally:
        db.close()

def get_context_financial_years(request, current_user=None, permissions=None, role_names=None):
    if not current_user:
        return []

    db = CommonSessionLocal()
    try:
        from app.modules.core.tenancy.models import FinancialYear
        tenant_id = int(get_active_tenant_id(request, current_user) or current_user.tenant_id)
        return db.execute(
            select(FinancialYear)
            .where(FinancialYear.tenant_id == tenant_id)
            .order_by(FinancialYear.start_date.desc(), FinancialYear.year_code.desc())
        ).scalars().all()
    finally:
        db.close()


templates.env.globals.update(
    can_view_users=can_view_users,
    can_view_own_alerts=can_view_own_alerts,
    can_manage_alerts=can_manage_alerts,
    can_view_notice_cases=can_view_notice_cases,
    can_manage_notice_cases=can_manage_notice_cases,
    can_upload_notice_case_documents=can_upload_notice_case_documents,
    can_download_notice_case_documents=can_download_notice_case_documents,
    can_delete_notice_case_documents=can_delete_notice_case_documents,
    can_view_employee_dashboard=can_view_employee_dashboard,
    can_view_employees=can_view_employees,
    can_manage_employees=can_manage_employees,
    can_change_employee_status=can_change_employee_status,
    can_switch_employee_tenant=can_switch_employee_tenant,
    can_switch_employee_branch=can_switch_employee_branch,
    can_view_employee_portal=can_view_employee_portal,
    can_edit_own_employee_profile=can_edit_own_employee_profile,
    can_view_own_employee_work=can_view_own_employee_work,
    can_manage_employee_work=can_manage_employee_work,
    can_view_employee_progress=can_view_employee_progress,
    can_request_employee_registration=can_request_employee_registration,
    can_approve_employee_registrations=can_approve_employee_registrations,
    can_punch_employee_attendance=can_punch_employee_attendance,
    can_view_own_employee_attendance=can_view_own_employee_attendance,
    can_view_all_employee_attendance=can_view_all_employee_attendance,
    can_approve_employee_attendance=can_approve_employee_attendance,
    can_apply_employee_leave=can_apply_employee_leave,
    can_view_own_employee_leave=can_view_own_employee_leave,
    can_view_all_employee_leave=can_view_all_employee_leave,
    can_approve_employee_leave=can_approve_employee_leave,
    can_manage_employee_leave_types=can_manage_employee_leave_types,
    can_manage_employee_leave_balances=can_manage_employee_leave_balances,
    can_view_own_employee_documents=can_view_own_employee_documents,
    can_upload_own_employee_documents=can_upload_own_employee_documents,
    can_view_all_employee_documents=can_view_all_employee_documents,
    can_manage_employee_documents=can_manage_employee_documents,
    can_verify_employee_documents=can_verify_employee_documents,
    can_manage_employee_document_types=can_manage_employee_document_types,
    can_view_employee_onboarding=can_view_employee_onboarding,
    can_manage_employee_onboarding=can_manage_employee_onboarding,
    can_approve_employee_onboarding=can_approve_employee_onboarding,
    can_view_employee_offboarding=can_view_employee_offboarding,
    can_manage_employee_offboarding=can_manage_employee_offboarding,
    can_approve_employee_offboarding=can_approve_employee_offboarding,
    can_request_own_employee_offboarding=can_request_own_employee_offboarding,
    can_import_employee_hr=can_import_employee_hr,
    can_manage_employee_payroll_structures=can_manage_employee_payroll_structures,
    can_run_employee_payroll=can_run_employee_payroll,
    can_view_employee_payroll=can_view_employee_payroll,
    can_view_own_employee_payslips=can_view_own_employee_payslips,
    can_approve_employee_payroll=can_approve_employee_payroll,
    can_manage_users=can_manage_users,
    can_view_consultants=can_view_consultants,
    can_manage_consultants=can_manage_consultants,
    can_link_consultant_clients=can_link_consultant_clients,
    can_manage_consultant_service_requests=can_manage_consultant_service_requests,
    can_manage_consultant_conversions=can_manage_consultant_conversions,
    can_view_consultant_portal=can_view_consultant_portal,
    can_manage_own_consultant_workspace=can_manage_own_consultant_workspace,
    can_view_settings=can_view_settings,
    can_manage_settings=can_manage_settings,
    can_view_rbac=can_view_rbac,
    can_manage_rbac=can_manage_rbac,
    can_view_audit=can_view_audit,
    can_view_tenants=can_view_tenants,
    can_manage_tenants=can_manage_tenants,
    can_view_branches=can_view_branches,
    can_manage_branches=can_manage_branches,
    can_change_branch_tenant=can_change_branch_tenant,
    can_view_services=can_view_services,
    can_manage_services=can_manage_services,
    can_manage_service_tasks=can_manage_service_tasks,
    can_import_services=can_import_services,
    can_import_service_tasks=can_import_service_tasks,
    can_switch_service_tenant=can_switch_service_tenant,
    can_switch_service_branch=can_switch_service_branch,
    can_view_clients=can_view_clients,
    can_view_billing=can_view_billing,
    can_create_billing=can_create_billing,
    can_generate_billing_invoices=can_generate_billing_invoices,
    can_view_billing_fee_structure=can_view_billing_fee_structure,
    can_import_billing_fee_structure=can_import_billing_fee_structure,
    can_view_platform_billing=can_view_platform_billing,
    can_manage_platform_billing=can_manage_platform_billing,
    can_generate_platform_billing=can_generate_platform_billing,
    can_manage_platform_plans=can_manage_platform_plans,
    can_manage_platform_subscriptions=can_manage_platform_subscriptions,
    can_view_marketplace_leads=can_view_marketplace_leads,
    can_create_marketplace_leads=can_create_marketplace_leads,
    can_assign_marketplace_leads=can_assign_marketplace_leads,
    can_update_marketplace_leads=can_update_marketplace_leads,
    can_convert_marketplace_leads=can_convert_marketplace_leads,
    can_view_documents=can_view_documents,
    can_upload_documents=can_upload_documents,
    can_download_documents=can_download_documents,
    can_delete_documents=can_delete_documents,
    can_manage_clients=can_manage_clients,
    can_export_clients=can_export_clients,
    can_switch_client_tenant=can_switch_client_tenant,
    can_switch_client_branch=can_switch_client_branch,
    get_unread_alert_count=get_unread_alert_count,
    get_current_tenant_name=get_current_tenant_name,
    get_current_branch_name=get_current_branch_name,
    get_current_firm_branding=get_current_firm_branding,
    get_domain_context=get_domain_context,
    get_user_profile_photo_url=get_user_profile_photo_url,
    get_user_initials=get_user_initials,
    get_client_sidebar_auditor_card=get_client_sidebar_auditor_card,
    get_context_tenants=get_context_tenants,
    get_context_branches=get_context_branches,
    get_context_financial_years=get_context_financial_years,
    get_active_tenant_id=get_active_tenant_id,
    get_active_tenant_code=get_active_tenant_code,
    get_active_branch_id=get_active_branch_id,
    get_active_branch_code=get_active_branch_code,
    get_active_financial_year=get_active_financial_year,
    get_active_assessment_year=get_active_assessment_year,
    build_page_url=build_page_url,
)
