from __future__ import annotations

from pathlib import Path
from typing import Annotated
from urllib.parse import urlparse

import typer

from resume_agent.agent import build_profile, load_profile, save_json, tailor_resume
from resume_agent.documents import read_document
from resume_agent.llm import OpenAILLM
from resume_agent.render import render_report, render_resume
from resume_agent.web import fetch_job_page

app = typer.Typer(no_args_is_help=True, help="Evidence-backed resume tailoring agent.")
profile_app = typer.Typer(no_args_is_help=True, help="Build and inspect your career profile.")
app.add_typer(profile_app, name="profile")

DEFAULT_PROFILE = Path(".resume-agent/profile.json")


def _llm(model: str | None) -> OpenAILLM:
    return OpenAILLM(model=model)


def _read_job(value: str) -> str:
    candidate_path = Path(value).expanduser()
    if candidate_path.is_file():
        return read_document(candidate_path)
    if urlparse(value).scheme in {"http", "https"}:
        return fetch_job_page(value)
    if len(value.strip()) < 80:
        raise typer.BadParameter(
            "Pass a job URL, a job-post file, or the full job text (at least 80 characters)."
        )
    return value.strip()


@profile_app.command("build")
def profile_build(
    resume: Annotated[Path, typer.Argument(exists=True, readable=True, help="Master resume file.")],
    about: Annotated[
        str, typer.Option(help="Extra factual career context not present in the resume.")
    ] = "",
    about_file: Annotated[Path | None, typer.Option(exists=True, readable=True)] = None,
    out: Annotated[Path, typer.Option(help="Canonical profile JSON path.")] = DEFAULT_PROFILE,
    model: Annotated[str | None, typer.Option(help="Override the OpenAI model.")] = None,
) -> None:
    """Extract a reusable, evidence-backed career profile."""
    notes = about
    if about_file:
        notes = f"{notes}\n{read_document(about_file)}".strip()
    profile = build_profile(_llm(model), read_document(resume), notes)
    save_json(profile, out)
    typer.echo(f"Profile saved to {out} with {len(profile.facts)} evidence facts.")
    if profile.unanswered_questions:
        typer.echo(f"{len(profile.unanswered_questions)} follow-up questions remain.")


@profile_app.command("show")
def profile_show(
    profile_path: Annotated[
        Path, typer.Option("--profile", exists=True, readable=True)
    ] = DEFAULT_PROFILE,
) -> None:
    """Show the canonical profile without calling the LLM."""
    profile = load_profile(profile_path)
    typer.echo(profile.model_dump_json(indent=2))


@app.command()
def models() -> None:
    """List model IDs available from the configured API endpoint."""
    for model_id in _llm(None).list_models():
        typer.echo(model_id)


@app.command()
def serve(
    host: Annotated[str, typer.Option(help="Interface to bind.")] = "127.0.0.1",
    port: Annotated[int, typer.Option(help="Port to bind.")] = 8000,
    reload: Annotated[bool, typer.Option(help="Reload when source files change.")] = False,
) -> None:
    """Launch the local Resume Agent web interface."""
    import uvicorn

    uvicorn.run("resume_agent.webapp:app", host=host, port=port, reload=reload)


@app.command()
def editor_build() -> None:
    """Install and build the bundled Oh My CV editor."""
    import os
    import subprocess

    project_root = Path(__file__).resolve().parents[2]
    editor_root = project_root / "vendor/oh-my-cv"
    if not editor_root.is_dir():
        raise typer.BadParameter("vendor/oh-my-cv is missing.")

    env = {**os.environ, "NUXT_PUBLIC_SIGNAL_API_BASE": ""}
    subprocess.run(
        [
            "pnpm",
            "install",
            "--frozen-lockfile",
            "--registry=https://registry.npmjs.org",
        ],
        cwd=editor_root,
        check=True,
        env=env,
    )
    subprocess.run(
        ["pnpm", "build-fast:pkg"],
        cwd=editor_root,
        check=True,
        env=env,
    )
    subprocess.run(["pnpm", "build"], cwd=editor_root, check=True, env=env)
    typer.echo("Oh My CV built successfully. Restart `resume-agent serve` to enable /cv/.")


@app.command()
def tailor(
    job: Annotated[str, typer.Argument(help="Job URL, file path, or pasted job post.")],
    profile_path: Annotated[
        Path, typer.Option("--profile", exists=True, readable=True)
    ] = DEFAULT_PROFILE,
    out_dir: Annotated[Path, typer.Option(help="Output directory.")] = Path("output"),
    model: Annotated[str | None, typer.Option(help="Override the OpenAI model.")] = None,
    strength: Annotated[
        int,
        typer.Option(
            min=0,
            max=100,
            help="Truthful tailoring strength: 0 preserves wording; 100 maximizes supported fit.",
        ),
    ] = 50,
    evidence_mode: Annotated[
        str,
        typer.Option(
            help="Evidence mode: strict claim-level IDs or profile-based flexible rewriting.",
        ),
    ] = "strict",
) -> None:
    """Tailor the resume to a job and run a second factuality audit."""
    profile = load_profile(profile_path)
    package = tailor_resume(
        _llm(model),
        profile,
        _read_job(job),
        strength,
        evidence_mode,
    )
    out_dir.mkdir(parents=True, exist_ok=True)
    save_json(package, out_dir / "tailoring.json")
    (out_dir / "resume.md").write_text(render_resume(package), encoding="utf-8")
    (out_dir / "resume-audited.md").write_text(
        render_resume(package, include_evidence=True), encoding="utf-8"
    )
    (out_dir / "report.md").write_text(render_report(package), encoding="utf-8")
    typer.echo(f"Tailored resume and audit report saved in {out_dir}.")


def main() -> None:
    app()


if __name__ == "__main__":
    main()
