from __future__ import annotations

from fastapi import APIRouter, Form, Request
from fastapi.responses import RedirectResponse

from app.core.db.common import CommonSessionLocal
from app.core.security.csrf import get_or_create_csrf_token, validate_csrf
from app.core.security.session_auth import get_current_user
from app.core.templating import templates
from app.modules.services.models import ClientServiceSubscription
from app.modules.services.client_services import (
    SUBSCRIPTION_STATUSES,
    attach_engagement_to_plan,
    get_enabled_firm_service,
    get_existing_subscription,
    get_subscription,
    list_assignable_users,
    list_clients_for_assignment,
    list_enabled_services_for_assignment,
    list_subscription_payload,
    parse_date,
)
from app.modules.core.rbac.deps import get_user_permissions, get_user_roles
from app.modules.core.rbac.permission_guard import require_permission

router = APIRouter(prefix="/services/client-services", tags=["services-client-services-ui"])


def _base_ctx(request: Request, user, db, **ctx):
    base = {
        "request": request,
        "current_user": user,
        "current_user_roles": get_user_roles(db, user.id),
        "current_user_permissions": get_user_permissions(db, user.id),
        "csrf_token": get_or_create_csrf_token(request),
        "subscription_statuses": SUBSCRIPTION_STATUSES,
    }
    base.update(ctx)
    return base


def _render(request: Request, template: str, db, user, **ctx):
    return templates.TemplateResponse(template, _base_ctx(request, user, db, **ctx))


def _redirect_denied():
    from app.core.http_responses import ui_access_denied
    return ui_access_denied()


def _has_perm(db, user, code: str) -> bool:
    try:
        require_permission(db, user, code)
        return True
    except Exception:
        return False


def _active_tenant_id(request: Request, user) -> int:
    return int(
        request.session.get("active_tenant_id")
        or request.session.get("selected_tenant_id")
        or request.session.get("tenant_id")
        or user.tenant_id
    )


def _active_branch_id(request: Request, user, db) -> int | None:
    value = request.session.get("active_branch_id")
    if value in (None, "", 0, "0"):
        if _has_perm(db, user, "clients.cross_branch"):
            return None
        return int(getattr(user, "branch_id", 0) or 0) or None
    return int(value)


def _locked_partner_id(db, user) -> int | None:
    return int(user.id) if _has_perm(db, user, "clients.view.own_only") else None


def _can_manage_client_services(db, user) -> bool:
    return _has_perm(db, user, "clients.edit")


@router.get("")
def subscription_list(request: Request, q: str = "", include_inactive: bool = True):
    db = CommonSessionLocal()
    try:
        user = get_current_user(request, db=db)
        if not user:
            return RedirectResponse(url="/login", status_code=303)
        try:
            require_permission(db, user, "clients.view")
        except Exception:
            return _redirect_denied()

        tenant_id = _active_tenant_id(request, user)
        branch_id = _active_branch_id(request, user, db)
        rows = list_subscription_payload(
            db,
            tenant_id=tenant_id,
            branch_id=branch_id,
            q=q,
            include_inactive=include_inactive,
        )
        return _render(
            request,
            "modules/services/templates/services/client_services/list.html",
            db,
            user,
            title="Client Service Subscriptions",
            rows=rows,
            q=q,
            include_inactive=include_inactive,
            can_manage=_can_manage_client_services(db, user),
        )
    finally:
        db.close()


@router.get("/new")
def subscription_create_page(request: Request, client_id: int | None = None):
    db = CommonSessionLocal()
    try:
        user = get_current_user(request, db=db)
        if not user:
            return RedirectResponse(url="/login", status_code=303)
        try:
            require_permission(db, user, "clients.edit")
        except Exception:
            return _redirect_denied()

        tenant_id = _active_tenant_id(request, user)
        branch_id = _active_branch_id(request, user, db)
        clients = list_clients_for_assignment(
            db,
            tenant_id=tenant_id,
            branch_id=branch_id,
            partner_id=_locked_partner_id(db, user),
        )
        enabled_services = list_enabled_services_for_assignment(db, tenant_id=tenant_id)
        assignable_users = list_assignable_users(db, tenant_id=tenant_id, branch_id=branch_id)

        return _render(
            request,
            "modules/services/templates/services/client_services/form.html",
            db,
            user,
            title="Assign Service to Client",
            mode="create",
            subscription=None,
            clients=clients,
            enabled_services=enabled_services,
            assignable_users=assignable_users,
            selected_client_id=client_id,
        )
    finally:
        db.close()


@router.post("/new")
def subscription_create_submit(
    request: Request,
    client_id: int = Form(...),
    service_catalogue_id: int = Form(...),
    assigned_partner_user_id: str = Form(""),
    assigned_manager_user_id: str = Form(""),
    assigned_staff_user_id: str = Form(""),
    start_date: str = Form(""),
    end_date: str = Form(""),
    status: str = Form("active"),
    remarks: str = Form(""),
    is_active: str | None = Form(None),
    csrf_token: str = Form(...),
):
    validate_csrf(request, csrf_token)
    db = CommonSessionLocal()
    try:
        user = get_current_user(request, db=db)
        if not user:
            return RedirectResponse(url="/login", status_code=303)
        try:
            require_permission(db, user, "clients.edit")
        except Exception:
            return _redirect_denied()

        tenant_id = _active_tenant_id(request, user)
        firm_selection = get_enabled_firm_service(db, tenant_id=tenant_id, service_catalogue_id=service_catalogue_id)
        if not firm_selection:
            return RedirectResponse(url="/services/client-services/new", status_code=303)

        existing = get_existing_subscription(
            db,
            tenant_id=tenant_id,
            client_id=client_id,
            service_catalogue_id=service_catalogue_id,
        )
        if existing:
            row = existing
        else:
            row = ClientServiceSubscription(
                tenant_id=tenant_id,
                client_id=client_id,
                service_catalogue_id=service_catalogue_id,
                created_by_user_id=user.id,
            )
            db.add(row)

        row.branch_id = firm_selection.default_branch_id or getattr(user, "branch_id", None)
        row.firm_service_selection_id = firm_selection.id
        row.assigned_partner_user_id = int(assigned_partner_user_id) if assigned_partner_user_id.strip() else None
        row.assigned_manager_user_id = int(assigned_manager_user_id) if assigned_manager_user_id.strip() else None
        row.assigned_staff_user_id = int(assigned_staff_user_id) if assigned_staff_user_id.strip() else None
        row.start_date = parse_date(start_date)
        row.end_date = parse_date(end_date)
        row.status = status or "active"
        row.remarks = remarks.strip() or None
        row.is_active = is_active is not None
        row.updated_by_user_id = user.id
        client = db.get(__import__("app.modules.clients.models", fromlist=["Client"]).Client, client_id)
        if client is not None:
            attach_engagement_to_plan(
                db, engagement=row, client=client, catalogue=firm_selection.catalogue,
                firm_selection=firm_selection, actor_user_id=user.id,
            )

        db.commit()
        db.refresh(row)
        return RedirectResponse(url=f"/services/client-services/{row.id}", status_code=303)
    finally:
        db.close()


@router.get("/{subscription_id}")
def subscription_detail(request: Request, subscription_id: int):
    db = CommonSessionLocal()
    try:
        user = get_current_user(request, db=db)
        if not user:
            return RedirectResponse(url="/login", status_code=303)
        try:
            require_permission(db, user, "clients.view")
        except Exception:
            return _redirect_denied()

        tenant_id = _active_tenant_id(request, user)
        row = get_subscription(db, subscription_id=subscription_id, tenant_id=tenant_id)
        if not row:
            return RedirectResponse(url="/services/client-services", status_code=303)

        return _render(
            request,
            "modules/services/templates/services/client_services/detail.html",
            db,
            user,
            title="Client Service Subscription",
            row=row,
            can_manage=_can_manage_client_services(db, user),
        )
    finally:
        db.close()


@router.get("/{subscription_id}/edit")
def subscription_edit_page(request: Request, subscription_id: int):
    db = CommonSessionLocal()
    try:
        user = get_current_user(request, db=db)
        if not user:
            return RedirectResponse(url="/login", status_code=303)
        try:
            require_permission(db, user, "clients.edit")
        except Exception:
            return _redirect_denied()

        tenant_id = _active_tenant_id(request, user)
        branch_id = _active_branch_id(request, user, db)
        row = get_subscription(db, subscription_id=subscription_id, tenant_id=tenant_id)
        if not row:
            return RedirectResponse(url="/services/client-services", status_code=303)

        clients = list_clients_for_assignment(
            db,
            tenant_id=tenant_id,
            branch_id=branch_id,
            partner_id=_locked_partner_id(db, user),
        )
        enabled_services = list_enabled_services_for_assignment(db, tenant_id=tenant_id)
        assignable_users = list_assignable_users(db, tenant_id=tenant_id, branch_id=branch_id)
        return _render(
            request,
            "modules/services/templates/services/client_services/form.html",
            db,
            user,
            title="Edit Client Service Subscription",
            mode="edit",
            subscription=row,
            clients=clients,
            enabled_services=enabled_services,
            assignable_users=assignable_users,
            selected_client_id=row.client_id,
        )
    finally:
        db.close()


@router.post("/{subscription_id}/edit")
def subscription_edit_submit(
    request: Request,
    subscription_id: int,
    assigned_partner_user_id: str = Form(""),
    assigned_manager_user_id: str = Form(""),
    assigned_staff_user_id: str = Form(""),
    start_date: str = Form(""),
    end_date: str = Form(""),
    status: str = Form("active"),
    remarks: str = Form(""),
    is_active: str | None = Form(None),
    csrf_token: str = Form(...),
):
    validate_csrf(request, csrf_token)
    db = CommonSessionLocal()
    try:
        user = get_current_user(request, db=db)
        if not user:
            return RedirectResponse(url="/login", status_code=303)
        try:
            require_permission(db, user, "clients.edit")
        except Exception:
            return _redirect_denied()

        tenant_id = _active_tenant_id(request, user)
        row = get_subscription(db, subscription_id=subscription_id, tenant_id=tenant_id)
        if not row:
            return RedirectResponse(url="/services/client-services", status_code=303)

        row.assigned_partner_user_id = int(assigned_partner_user_id) if assigned_partner_user_id.strip() else None
        row.assigned_manager_user_id = int(assigned_manager_user_id) if assigned_manager_user_id.strip() else None
        row.assigned_staff_user_id = int(assigned_staff_user_id) if assigned_staff_user_id.strip() else None
        row.start_date = parse_date(start_date)
        row.end_date = parse_date(end_date)
        row.status = status or "active"
        row.remarks = remarks.strip() or None
        row.is_active = is_active is not None
        row.updated_by_user_id = user.id
        db.commit()
        return RedirectResponse(url=f"/services/client-services/{row.id}", status_code=303)
    finally:
        db.close()
