from __future__ import annotations

from dataclasses import dataclass
import sys
from pathlib import Path
from typing import List

from playwright.sync_api import Page

from ....core.artifacts import create_run_dir, shot
from ....core.runtime import SESSION_POOL, MFARequired, open_one_shot
from ....auth.session import ensure_logged_in
from ....core.log import log_step, pause
from ..flows import nav as users_nav
from ..flows import select_user as select_user_flow
from ..flows import upload_docs as upload_flow
from ..flows.upload_docs import DocSpec


_ALLOWED_FILTERS = {
    "Internal Only",
    "Internal Admin Only (Legacy)",
    "Internal Admin and Manager Only (Legacy)",
    "Show Client",
    "Show Contractor",
    "Show All",
}


def _parse_doc_spec(s: str) -> DocSpec:
    parts = [p.strip() for p in s.split(";") if p.strip()]
    kv = {}
    for part in parts:
        if "=" not in part:
            raise ValueError(f"Invalid --doc chunk (expected key=value): {part!r}")
        k, v = part.split("=", 1)
        kv[k.strip().lower()] = v.strip()

    if not kv.get("file"):
        raise ValueError(f"--doc missing required 'file=': {s!r}")

    f = Path(kv["file"]).expanduser().resolve()
    c = kv.get("comment", "")
    flt = kv.get("filter", "")

    if flt and flt not in _ALLOWED_FILTERS:
        raise ValueError(f"Invalid filter in --doc: {flt!r}. Allowed: {sorted(_ALLOWED_FILTERS)}")

    return DocSpec(file=f, comment=c, filter=flt)


def _build_docs(cfg) -> List[DocSpec]:
    doc_specs = list(getattr(cfg, "user_docs", []) or [])
    if doc_specs:
        return [_parse_doc_spec(s) for s in doc_specs]

    files = list(getattr(cfg, "user_files", []) or [])
    if not files:
        raise SystemExit("Missing files. Use one or more --doc ... OR --file /path/to/doc.pdf")

    comments = list(getattr(cfg, "user_comments", []) or [])
    filters = list(getattr(cfg, "user_filters", []) or [])

    docs: List[DocSpec] = []
    for i, f in enumerate(files):
        fp = Path(f).expanduser().resolve()
        c = comments[i] if i < len(comments) else ""
        flt = filters[i] if i < len(filters) else ""
        if flt and flt not in _ALLOWED_FILTERS:
            raise SystemExit(f"Invalid --filter: {flt!r}. Allowed: {sorted(_ALLOWED_FILTERS)}")
        docs.append(DocSpec(file=fp, comment=c, filter=flt))

    return docs


def cmd_users_upload_documents(cfg, mfa_code: str = "") -> int:
    run_dir = create_run_dir(cfg, "users-upload-docs")

    if not getattr(cfg, "user_email", "").strip():
        raise SystemExit("Missing --user-email (required).")

    docs = _build_docs(cfg)
    for d in docs:
        if not Path(d.file).exists():
            raise SystemExit(f"Upload file does not exist: {d.file}")

    try:
        interactive = sys.stdin.isatty()
    except Exception:
        interactive = False
    allow_mfa = (
        bool(mfa_code.strip())
        or bool(getattr(cfg, "pause_on_mfa", False))
        or interactive
    )

    tenant_id = cfg.tenant_id or "<default>"

    if cfg.reuse_session:
        lock = SESSION_POOL.tenant_lock(tenant_id)
        with lock:
            try:
                rt = SESSION_POOL.get(
                    cfg=cfg,
                    tenant_id=tenant_id,
                    run_dir=run_dir,
                    mfa_code=mfa_code,
                    allow_mfa=allow_mfa,
                )
            except MFARequired:
                shot(rt.page, run_dir, "mfa-required") if "rt" in locals() else None
                raise SystemExit(
                    "MFA_REQUIRED: AroFlo pidió MFA. Reintenta con --pause-on-mfa para ingresarlo "
                    "por consola, o pásalo directo con --mfa-code 123456."
                )

            page: Page = rt.page
            try:
                users_nav.run(page, cfg, run_dir, select_user=False)
                # Si ya estamos en el perfil (thisUser=1), no reintentar buscar la tabla
                if "thisUser=1" not in (page.url or "") and page.locator("table#tblUsers").count() > 0:
                    result = select_user_flow.select_user_by_email(
                        page,
                        cfg.user_email.strip(),
                        run_dir=run_dir,
                    )
                    if not result.found:
                        shot(page, run_dir, "users-upload-user-not-found")
                        raise RuntimeError(f"User not found by email: {cfg.user_email}")

                shot(page, run_dir, "users-upload-01-user-selected")
                log_step("users-upload-01-user-selected", page)
                pause(cfg, "User profile opened. Ready to upload documents")

                upload_flow.users_upload_documents(
                    page,
                    docs=docs,
                    cfg=cfg,
                    run_dir=run_dir,
                    timeout_ms=45_000,
                )

                shot(page, run_dir, "users-upload-99-done")
                rt.save_state()
                rt.touch()
            except Exception:
                shot(page, run_dir, "99-error")
                raise
            finally:
                if not cfg.keep_open:
                    SESSION_POOL.close(tenant_id)
        return 0

    rt = open_one_shot(cfg)
    page = rt.page
    try:
        ensure_logged_in(page, cfg, run_dir, mfa_code=mfa_code, allow_mfa=allow_mfa)
        users_nav.run(page, cfg, run_dir, select_user=False)
        if "thisUser=1" not in (page.url or "") and page.locator("table#tblUsers").count() > 0:
            result = select_user_flow.select_user_by_email(
                page,
                cfg.user_email.strip(),
                run_dir=run_dir,
            )
            if not result.found:
                shot(page, run_dir, "users-upload-user-not-found")
                raise RuntimeError(f"User not found by email: {cfg.user_email}")

        shot(page, run_dir, "users-upload-01-user-selected")
        log_step("users-upload-01-user-selected", page)
        pause(cfg, "User profile opened. Ready to upload documents")

        upload_flow.users_upload_documents(
            page,
            docs=docs,
            cfg=cfg,
            run_dir=run_dir,
            timeout_ms=45_000,
        )

        shot(page, run_dir, "users-upload-99-done")
        rt.save_state()
    except Exception:
        shot(page, run_dir, "99-error")
        raise
    finally:
        rt.close()

    return 0
