from __future__ import annotations

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

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.core.rbac.deps import get_user_permissions, get_user_roles
from app.modules.domain_management.models import DomainMapping
from app.modules.domain_management.coolify_client import get_coolify_application_domains, sync_domain_to_coolify
from app.modules.domain_management.services import (
    DomainMappingPayload,
    create_domain_mapping,
    create_or_get_tenant_subdomain_mapping,
    create_or_get_firm_custom_domain_mapping,
    create_or_get_consultant_domain_mapping,
    list_consultant_domain_candidates,
    list_domain_mappings,
    list_domains_requiring_verification,
    verify_all_pending_domains,
    verify_domain_dns_txt,
    build_ssl_proxy_snippets,
    check_domain_ssl_certificate,
    list_ssl_domains,
    mark_ssl_managed,
    list_tenant_subdomain_candidates,
    mark_verified,
    reference_data,
    regenerate_verification_token,
    update_domain_mapping,
    validate_domain_payload,
)

router = APIRouter(prefix="/domains", tags=["domain-management-ui"])


def _is_domain_admin(db, user) -> bool:
    roles = set(get_user_roles(db, user.id))
    return "System Admin" in roles


def _base_ctx(request: Request, db, user, **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),
    }
    base.update(ctx)
    return base


def _require_user(request: Request, db):
    user = get_current_user(request, db=db)
    if not user:
        return None, RedirectResponse(url="/login", status_code=303)
    if not _is_domain_admin(db, user):
        return user, RedirectResponse(url="/system-settings", status_code=303)
    return user, None


def _to_int(value: str | int | None) -> int | None:
    try:
        n = int(value or 0)
        return n if n > 0 else None
    except Exception:
        return None


def _payload_from_form(
    *,
    domain_name: str,
    domain_type: str,
    tenant_id: str | int | None,
    branch_id: str | int | None,
    consultant_id: str | int | None,
    parent_tenant_id: str | int | None,
    is_primary: str | None,
    is_verified: str | None,
    status: str,
    ssl_mode: str,
    notes: str,
) -> DomainMappingPayload:
    return DomainMappingPayload(
        domain_name=domain_name,
        domain_type=domain_type,
        tenant_id=_to_int(tenant_id),
        branch_id=_to_int(branch_id),
        consultant_id=_to_int(consultant_id),
        parent_tenant_id=_to_int(parent_tenant_id),
        is_primary=bool(is_primary),
        is_verified=bool(is_verified),
        status=status or "draft",
        ssl_mode=ssl_mode or "manual",
        notes=notes,
    )


@router.get("")
def domain_list(request: Request, q: str = "", status: str = "", domain_type: str = ""):
    db = CommonSessionLocal()
    try:
        user, response = _require_user(request, db)
        if response:
            return response
        mappings = list_domain_mappings(db, q=q, status=status, domain_type=domain_type)
        refs = reference_data(db)
        return templates.TemplateResponse(
            "modules/domain_management/templates/domain_management/list.html",
            _base_ctx(
                request,
                db,
                user,
                title="Domain Mapping",
                mappings=mappings,
                filters={"q": q, "status": status, "domain_type": domain_type},
                **refs,
            ),
        )
    finally:
        db.close()


@router.get("/new")
def domain_create_page(request: Request):
    db = CommonSessionLocal()
    try:
        user, response = _require_user(request, db)
        if response:
            return response
        return templates.TemplateResponse(
            "modules/domain_management/templates/domain_management/form.html",
            _base_ctx(
                request,
                db,
                user,
                title="Add Domain Mapping",
                mapping=None,
                errors=[],
                form={},
                **reference_data(db),
            ),
        )
    finally:
        db.close()


@router.post("/new")
def domain_create_submit(
    request: Request,
    csrf_token: str = Form(...),
    domain_name: str = Form(...),
    domain_type: str = Form(...),
    tenant_id: str = Form(""),
    branch_id: str = Form(""),
    consultant_id: str = Form(""),
    parent_tenant_id: str = Form(""),
    is_primary: str | None = Form(None),
    is_verified: str | None = Form(None),
    status: str = Form("draft"),
    ssl_mode: str = Form("manual"),
    notes: str = Form(""),
):
    validate_csrf(request, csrf_token)
    db = CommonSessionLocal()
    try:
        user, response = _require_user(request, db)
        if response:
            return response
        payload = _payload_from_form(
            domain_name=domain_name,
            domain_type=domain_type,
            tenant_id=tenant_id,
            branch_id=branch_id,
            consultant_id=consultant_id,
            parent_tenant_id=parent_tenant_id,
            is_primary=is_primary,
            is_verified=is_verified,
            status=status,
            ssl_mode=ssl_mode,
            notes=notes,
        )
        errors = validate_domain_payload(db, payload)
        if errors:
            return templates.TemplateResponse(
                "modules/domain_management/templates/domain_management/form.html",
                _base_ctx(request, db, user, title="Add Domain Mapping", mapping=None, errors=errors, form=payload.__dict__, **reference_data(db)),
                status_code=400,
            )
        mapping = create_domain_mapping(db, payload, user_id=user.id)
        return RedirectResponse(url=f"/domains/{mapping.id}", status_code=303)
    finally:
        db.close()



@router.get("/tenant-subdomains")
def tenant_subdomain_page(request: Request, base_domain: str = "filingabc.com"):
    db = CommonSessionLocal()
    try:
        user, response = _require_user(request, db)
        if response:
            return response
        refs = reference_data(db)
        candidates = list_tenant_subdomain_candidates(db, base_domain=base_domain)
        return templates.TemplateResponse(
            "modules/domain_management/templates/domain_management/tenant_subdomains.html",
            _base_ctx(
                request,
                db,
                user,
                title="Tenant Subdomains",
                base_domain=base_domain,
                candidates=candidates,
                message="",
                errors=[],
                **refs,
            ),
        )
    finally:
        db.close()


@router.post("/tenant-subdomains/create")
def tenant_subdomain_create(
    request: Request,
    csrf_token: str = Form(...),
    tenant_id: str = Form(...),
    base_domain: str = Form("filingabc.com"),
    branch_id: str = Form(""),
    mark_verified_active: str | None = Form(None),
):
    validate_csrf(request, csrf_token)
    db = CommonSessionLocal()
    try:
        user, response = _require_user(request, db)
        if response:
            return response
        errors: list[str] = []
        message = ""
        try:
            mapping, created = create_or_get_tenant_subdomain_mapping(
                db,
                tenant_id=int(tenant_id),
                base_domain=base_domain,
                branch_id=_to_int(branch_id),
                mark_verified_active=bool(mark_verified_active),
                user_id=user.id,
            )
            if created:
                return RedirectResponse(url=f"/domains/{mapping.id}", status_code=303)
            message = f"Domain mapping already exists: {mapping.domain_name}"
        except Exception as exc:
            errors.append(str(exc))
        refs = reference_data(db)
        return templates.TemplateResponse(
            "modules/domain_management/templates/domain_management/tenant_subdomains.html",
            _base_ctx(
                request,
                db,
                user,
                title="Tenant Subdomains",
                base_domain=base_domain,
                candidates=list_tenant_subdomain_candidates(db, base_domain=base_domain),
                message=message,
                errors=errors,
                **refs,
            ),
            status_code=400 if errors else 200,
        )
    finally:
        db.close()


@router.get("/firm-domain")
def firm_custom_domain_page(request: Request, domain_name: str = "arrr.associates"):
    db = CommonSessionLocal()
    try:
        user, response = _require_user(request, db)
        if response:
            return response
        refs = reference_data(db)
        existing = None
        normalized = (domain_name or "arrr.associates").strip().lower().split("/", 1)[0].split(":", 1)[0].rstrip(".")
        if normalized:
            existing = db.execute(select(DomainMapping).where(DomainMapping.domain_name == normalized)).scalar_one_or_none()
        return templates.TemplateResponse(
            "modules/domain_management/templates/domain_management/firm_domain.html",
            _base_ctx(
                request,
                db,
                user,
                title="Audit Firm Custom Domain",
                domain_name=normalized or "arrr.associates",
                existing=existing,
                message="",
                errors=[],
                **refs,
            ),
        )
    finally:
        db.close()


@router.post("/firm-domain/create")
def firm_custom_domain_create(
    request: Request,
    csrf_token: str = Form(...),
    tenant_id: str = Form(...),
    domain_name: str = Form("arrr.associates"),
    branch_id: str = Form(""),
    mark_verified_active: str | None = Form(None),
):
    validate_csrf(request, csrf_token)
    db = CommonSessionLocal()
    try:
        user, response = _require_user(request, db)
        if response:
            return response
        errors: list[str] = []
        message = ""
        try:
            mapping, created = create_or_get_firm_custom_domain_mapping(
                db,
                tenant_id=int(tenant_id),
                domain_name=domain_name,
                branch_id=_to_int(branch_id),
                mark_verified_active=bool(mark_verified_active),
                user_id=user.id,
            )
            if created:
                return RedirectResponse(url=f"/domains/{mapping.id}", status_code=303)
            message = f"Domain mapping already exists: {mapping.domain_name}"
        except Exception as exc:
            errors.append(str(exc))
        refs = reference_data(db)
        normalized = (domain_name or "arrr.associates").strip().lower().split("/", 1)[0].split(":", 1)[0].rstrip(".")
        existing = None
        if normalized:
            existing = db.execute(select(DomainMapping).where(DomainMapping.domain_name == normalized)).scalar_one_or_none()
        return templates.TemplateResponse(
            "modules/domain_management/templates/domain_management/firm_domain.html",
            _base_ctx(
                request,
                db,
                user,
                title="Audit Firm Custom Domain",
                domain_name=normalized or "arrr.associates",
                existing=existing,
                message=message,
                errors=errors,
                **refs,
            ),
            status_code=400 if errors else 200,
        )
    finally:
        db.close()


@router.get("/consultant-domains")
def consultant_domains_page(request: Request, base_domain: str = "filingabc.com", firm_base_domain: str = "arrr.accountant"):
    db = CommonSessionLocal()
    try:
        user, response = _require_user(request, db)
        if response:
            return response
        refs = reference_data(db)
        candidates = list_consultant_domain_candidates(db, base_domain=base_domain, firm_base_domain=firm_base_domain)
        return templates.TemplateResponse(
            "modules/domain_management/templates/domain_management/consultant_domains.html",
            _base_ctx(
                request,
                db,
                user,
                title="Consultant Profile Domains",
                base_domain=base_domain,
                firm_base_domain=firm_base_domain,
                candidates=candidates,
                message="",
                errors=[],
                **refs,
            ),
        )
    finally:
        db.close()


@router.post("/consultant-domains/create")
def consultant_domain_create(
    request: Request,
    csrf_token: str = Form(...),
    consultant_id: str = Form(...),
    domain_type: str = Form("consultant_marketplace_subdomain"),
    domain_name: str = Form(""),
    base_domain: str = Form("filingabc.com"),
    firm_base_domain: str = Form("arrr.accountant"),
    parent_tenant_id: str = Form(""),
    mark_verified_active: str | None = Form(None),
):
    validate_csrf(request, csrf_token)
    db = CommonSessionLocal()
    try:
        user, response = _require_user(request, db)
        if response:
            return response
        errors: list[str] = []
        message = ""
        try:
            mapping, created = create_or_get_consultant_domain_mapping(
                db,
                consultant_id=int(consultant_id),
                domain_type=domain_type,
                domain_name=domain_name or None,
                base_domain=base_domain,
                firm_base_domain=firm_base_domain,
                parent_tenant_id=_to_int(parent_tenant_id),
                mark_verified_active=bool(mark_verified_active),
                user_id=user.id,
            )
            if created:
                return RedirectResponse(url=f"/domains/{mapping.id}", status_code=303)
            message = f"Domain mapping already exists: {mapping.domain_name}"
        except Exception as exc:
            errors.append(str(exc))
        refs = reference_data(db)
        return templates.TemplateResponse(
            "modules/domain_management/templates/domain_management/consultant_domains.html",
            _base_ctx(
                request,
                db,
                user,
                title="Consultant Profile Domains",
                base_domain=base_domain,
                firm_base_domain=firm_base_domain,
                candidates=list_consultant_domain_candidates(db, base_domain=base_domain, firm_base_domain=firm_base_domain),
                message=message,
                errors=errors,
                **refs,
            ),
            status_code=400 if errors else 200,
        )
    finally:
        db.close()


@router.get("/verification")
def domain_verification_page(request: Request):
    db = CommonSessionLocal()
    try:
        user, response = _require_user(request, db)
        if response:
            return response
        pending = list_domains_requiring_verification(db)
        return templates.TemplateResponse(
            "modules/domain_management/templates/domain_management/verification.html",
            _base_ctx(
                request,
                db,
                user,
                title="Custom Domain Verification",
                pending=pending,
                results=[],
            ),
        )
    finally:
        db.close()


@router.post("/verification/run")
def domain_verification_run(request: Request, csrf_token: str = Form(...), limit: str = Form("25")):
    validate_csrf(request, csrf_token)
    db = CommonSessionLocal()
    try:
        user, response = _require_user(request, db)
        if response:
            return response
        try:
            n = int(limit or 25)
        except Exception:
            n = 25
        results = verify_all_pending_domains(db, user_id=user.id, limit=n)
        return templates.TemplateResponse(
            "modules/domain_management/templates/domain_management/verification.html",
            _base_ctx(
                request,
                db,
                user,
                title="Custom Domain Verification",
                pending=list_domains_requiring_verification(db),
                results=results,
            ),
        )
    finally:
        db.close()


@router.get("/ssl")
def ssl_dashboard(request: Request):
    db = CommonSessionLocal()
    try:
        user, response = _require_user(request, db)
        if response:
            return response
        domains = list_ssl_domains(db)
        return templates.TemplateResponse(
            "modules/domain_management/templates/domain_management/ssl.html",
            _base_ctx(request, db, user, title="SSL Automation", domains=domains),
        )
    finally:
        db.close()


@router.post("/ssl/check-all")
def ssl_check_all(request: Request, csrf_token: str = Form(...)):
    validate_csrf(request, csrf_token)
    db = CommonSessionLocal()
    try:
        user, response = _require_user(request, db)
        if response:
            return response
        results = []
        for mapping in list_ssl_domains(db)[:25]:
            if mapping.status == "active" and mapping.is_verified:
                result = check_domain_ssl_certificate(db, mapping, user_id=user.id)
                results.append((mapping, result))
        domains = list_ssl_domains(db)
        return templates.TemplateResponse(
            "modules/domain_management/templates/domain_management/ssl.html",
            _base_ctx(request, db, user, title="SSL Automation", domains=domains, ssl_results=results),
        )
    finally:
        db.close()


@router.get("/coolify/status")
def coolify_status_page(request: Request):
    db = CommonSessionLocal()
    try:
        user, response = _require_user(request, db)
        if response:
            return response
        result = get_coolify_application_domains()
        domains = list_ssl_domains(db)
        return templates.TemplateResponse(
            "modules/domain_management/templates/domain_management/ssl.html",
            _base_ctx(request, db, user, title="Coolify Domain Sync", domains=domains, coolify_result=result),
            status_code=200 if result.ok else 400,
        )
    finally:
        db.close()


@router.get("/{mapping_id}/ssl")
def ssl_detail(request: Request, mapping_id: int):
    db = CommonSessionLocal()
    try:
        user, response = _require_user(request, db)
        if response:
            return response
        mapping = db.execute(select(DomainMapping).where(DomainMapping.id == mapping_id)).scalar_one_or_none()
        if not mapping:
            return RedirectResponse(url="/domains/ssl", status_code=303)
        snippets = build_ssl_proxy_snippets(mapping)
        return templates.TemplateResponse(
            "modules/domain_management/templates/domain_management/ssl_detail.html",
            _base_ctx(request, db, user, title=f"SSL: {mapping.domain_name}", mapping=mapping, snippets=snippets),
        )
    finally:
        db.close()


@router.post("/{mapping_id}/coolify/sync")
def coolify_sync_one(request: Request, mapping_id: int, csrf_token: str = Form(...)):
    validate_csrf(request, csrf_token)
    db = CommonSessionLocal()
    try:
        user, response = _require_user(request, db)
        if response:
            return response
        mapping = db.execute(select(DomainMapping).where(DomainMapping.id == mapping_id)).scalar_one_or_none()
        if not mapping:
            return RedirectResponse(url="/domains", status_code=303)
        result = sync_domain_to_coolify(db, mapping, user_id=user.id)
        db.refresh(mapping)
        return templates.TemplateResponse(
            "modules/domain_management/templates/domain_management/detail.html",
            _base_ctx(request, db, user, title=mapping.domain_name, mapping=mapping, coolify_result=result),
            status_code=200 if result.ok else 400,
        )
    finally:
        db.close()


@router.post("/{mapping_id}/coolify/check")
def coolify_check_one(request: Request, mapping_id: int, csrf_token: str = Form(...)):
    validate_csrf(request, csrf_token)
    db = CommonSessionLocal()
    try:
        user, response = _require_user(request, db)
        if response:
            return response
        mapping = db.execute(select(DomainMapping).where(DomainMapping.id == mapping_id)).scalar_one_or_none()
        if not mapping:
            return RedirectResponse(url="/domains", status_code=303)
        result = get_coolify_application_domains()
        return templates.TemplateResponse(
            "modules/domain_management/templates/domain_management/detail.html",
            _base_ctx(request, db, user, title=mapping.domain_name, mapping=mapping, coolify_result=result),
            status_code=200 if result.ok else 400,
        )
    finally:
        db.close()


@router.post("/{mapping_id}/ssl/check")
def ssl_check_one(request: Request, mapping_id: int, csrf_token: str = Form(...)):
    validate_csrf(request, csrf_token)
    db = CommonSessionLocal()
    try:
        user, response = _require_user(request, db)
        if response:
            return response
        mapping = db.execute(select(DomainMapping).where(DomainMapping.id == mapping_id)).scalar_one_or_none()
        if not mapping:
            return RedirectResponse(url="/domains/ssl", status_code=303)
        result = check_domain_ssl_certificate(db, mapping, user_id=user.id)
        snippets = build_ssl_proxy_snippets(mapping)
        return templates.TemplateResponse(
            "modules/domain_management/templates/domain_management/ssl_detail.html",
            _base_ctx(request, db, user, title=f"SSL: {mapping.domain_name}", mapping=mapping, snippets=snippets, ssl_result=result),
            status_code=200 if result.ok else 400,
        )
    finally:
        db.close()


@router.post("/{mapping_id}/ssl/mark-managed")
def ssl_mark_managed(request: Request, mapping_id: int, csrf_token: str = Form(...), provider: str = Form("manual")):
    validate_csrf(request, csrf_token)
    db = CommonSessionLocal()
    try:
        user, response = _require_user(request, db)
        if response:
            return response
        mapping = db.execute(select(DomainMapping).where(DomainMapping.id == mapping_id)).scalar_one_or_none()
        if mapping:
            mark_ssl_managed(db, mapping, provider=provider, user_id=user.id)
        return RedirectResponse(url=f"/domains/{mapping_id}/ssl", status_code=303)
    finally:
        db.close()


@router.get("/{mapping_id}")
def domain_detail(request: Request, mapping_id: int):
    db = CommonSessionLocal()
    try:
        user, response = _require_user(request, db)
        if response:
            return response
        mapping = db.execute(select(DomainMapping).where(DomainMapping.id == mapping_id)).scalar_one_or_none()
        if not mapping:
            return RedirectResponse(url="/domains", status_code=303)
        return templates.TemplateResponse(
            "modules/domain_management/templates/domain_management/detail.html",
            _base_ctx(request, db, user, title=mapping.domain_name, mapping=mapping),
        )
    finally:
        db.close()


@router.get("/{mapping_id}/edit")
def domain_edit_page(request: Request, mapping_id: int):
    db = CommonSessionLocal()
    try:
        user, response = _require_user(request, db)
        if response:
            return response
        mapping = db.execute(select(DomainMapping).where(DomainMapping.id == mapping_id)).scalar_one_or_none()
        if not mapping:
            return RedirectResponse(url="/domains", status_code=303)
        return templates.TemplateResponse(
            "modules/domain_management/templates/domain_management/form.html",
            _base_ctx(request, db, user, title="Edit Domain Mapping", mapping=mapping, errors=[], form={}, **reference_data(db)),
        )
    finally:
        db.close()


@router.post("/{mapping_id}/edit")
def domain_edit_submit(
    request: Request,
    mapping_id: int,
    csrf_token: str = Form(...),
    domain_name: str = Form(...),
    domain_type: str = Form(...),
    tenant_id: str = Form(""),
    branch_id: str = Form(""),
    consultant_id: str = Form(""),
    parent_tenant_id: str = Form(""),
    is_primary: str | None = Form(None),
    is_verified: str | None = Form(None),
    status: str = Form("draft"),
    ssl_mode: str = Form("manual"),
    notes: str = Form(""),
):
    validate_csrf(request, csrf_token)
    db = CommonSessionLocal()
    try:
        user, response = _require_user(request, db)
        if response:
            return response
        mapping = db.execute(select(DomainMapping).where(DomainMapping.id == mapping_id)).scalar_one_or_none()
        if not mapping:
            return RedirectResponse(url="/domains", status_code=303)
        payload = _payload_from_form(
            domain_name=domain_name,
            domain_type=domain_type,
            tenant_id=tenant_id,
            branch_id=branch_id,
            consultant_id=consultant_id,
            parent_tenant_id=parent_tenant_id,
            is_primary=is_primary,
            is_verified=is_verified,
            status=status,
            ssl_mode=ssl_mode,
            notes=notes,
        )
        errors = validate_domain_payload(db, payload, mapping_id=mapping.id)
        if errors:
            return templates.TemplateResponse(
                "modules/domain_management/templates/domain_management/form.html",
                _base_ctx(request, db, user, title="Edit Domain Mapping", mapping=mapping, errors=errors, form=payload.__dict__, **reference_data(db)),
                status_code=400,
            )
        update_domain_mapping(db, mapping, payload, user_id=user.id)
        return RedirectResponse(url=f"/domains/{mapping.id}", status_code=303)
    finally:
        db.close()


@router.post("/{mapping_id}/verify-dns")
def domain_verify_dns(request: Request, mapping_id: int, csrf_token: str = Form(...)):
    validate_csrf(request, csrf_token)
    db = CommonSessionLocal()
    try:
        user, response = _require_user(request, db)
        if response:
            return response
        mapping = db.execute(select(DomainMapping).where(DomainMapping.id == mapping_id)).scalar_one_or_none()
        if not mapping:
            return RedirectResponse(url="/domains", status_code=303)
        result = verify_domain_dns_txt(db, mapping, user_id=user.id)
        return templates.TemplateResponse(
            "modules/domain_management/templates/domain_management/detail.html",
            _base_ctx(request, db, user, title=mapping.domain_name, mapping=mapping, dns_result=result),
            status_code=200 if result.ok else 400,
        )
    finally:
        db.close()


@router.post("/{mapping_id}/regenerate-token")
def domain_regenerate_token(request: Request, mapping_id: int, csrf_token: str = Form(...)):
    validate_csrf(request, csrf_token)
    db = CommonSessionLocal()
    try:
        user, response = _require_user(request, db)
        if response:
            return response
        mapping = db.execute(select(DomainMapping).where(DomainMapping.id == mapping_id)).scalar_one_or_none()
        if mapping:
            regenerate_verification_token(db, mapping, user_id=user.id)
        return RedirectResponse(url=f"/domains/{mapping_id}", status_code=303)
    finally:
        db.close()


@router.post("/{mapping_id}/mark-verified")
def domain_mark_verified(request: Request, mapping_id: int, csrf_token: str = Form(...)):
    validate_csrf(request, csrf_token)
    db = CommonSessionLocal()
    try:
        user, response = _require_user(request, db)
        if response:
            return response
        mapping = db.execute(select(DomainMapping).where(DomainMapping.id == mapping_id)).scalar_one_or_none()
        if mapping:
            mark_verified(db, mapping, user_id=user.id)
        return RedirectResponse(url=f"/domains/{mapping_id}", status_code=303)
    finally:
        db.close()
