"""파이프라인 진행률 추적 테스트."""

import io
import shutil

import pytest
from fastapi.testclient import TestClient
from fpdf import FPDF
from pathlib import Path
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker

from app.core.config import settings
from app.core.database import Base, engine, SessionLocal
from app.main import app
from app.models.project import PipelineProgress
from app.modules.progress_tracker import ProgressTracker
from tests._safety_guards import safe_drop_all, safe_rmtree


def make_test_pdf(text="Test screenplay content") -> bytes:
    pdf = FPDF()
    pdf.add_page()
    pdf.set_font("Helvetica", size=12)
    pdf.cell(200, 10, text=text)
    return pdf.output()


# ------------------------------------------------------------------
# Unit tests -- ProgressTracker + PipelineProgress model
# ------------------------------------------------------------------


@pytest.fixture()
def pdb():
    """In-memory DB session for unit tests."""
    test_engine = create_engine("sqlite:///:memory:")
    Base.metadata.create_all(test_engine)
    Session = sessionmaker(bind=test_engine)
    session = Session()
    yield session
    session.close()
    test_engine.dispose()


def test_pipeline_progress_model(pdb):
    """PipelineProgress row can be created and queried."""
    from datetime import datetime, timezone
    now = datetime.now(timezone.utc).isoformat()
    row = PipelineProgress(
        id="test-id",
        project_id="test-project",
        episode_id="ep-1",
        operation="analysis",
        status="running",
        current_step="step 1",
        total_steps=3,
        completed_steps=1,
        started_at=now,
        updated_at=now,
    )
    pdb.add(row)
    pdb.commit()

    result = pdb.query(PipelineProgress).filter(PipelineProgress.id == "test-id").first()
    assert result is not None
    assert result.episode_id == "ep-1"
    assert result.operation == "analysis"
    assert result.status == "running"
    assert result.completed_steps == 1
    assert result.total_steps == 3


def test_progress_tracker_create(pdb):
    """ProgressTracker creates a running row on init."""
    tracker = ProgressTracker(pdb, "ep-1", "analysis", "test-project")
    row = pdb.query(PipelineProgress).filter(PipelineProgress.id == tracker.progress_id).first()
    assert row is not None
    assert row.status == "running"
    assert row.episode_id == "ep-1"
    assert row.operation == "analysis"


def test_progress_tracker_update(pdb):
    """ProgressTracker.update() modifies step/counts."""
    tracker = ProgressTracker(pdb, "ep-1", "analysis", "test-project")
    tracker.update("엔티티 추출 중", 1, 3)

    row = pdb.query(PipelineProgress).filter(PipelineProgress.id == tracker.progress_id).first()
    assert row.current_step == "엔티티 추출 중"
    assert row.completed_steps == 1
    assert row.total_steps == 3
    assert row.status == "running"


def test_progress_tracker_complete(pdb):
    """ProgressTracker.complete() marks status completed."""
    tracker = ProgressTracker(pdb, "ep-1", "image_generation", "test-project")
    tracker.update("generating", 5, 10)
    tracker.complete()

    row = pdb.query(PipelineProgress).filter(PipelineProgress.id == tracker.progress_id).first()
    assert row.status == "completed"
    assert row.completed_at is not None


def test_progress_tracker_fail(pdb):
    """ProgressTracker.fail() marks status error."""
    tracker = ProgressTracker(pdb, "ep-1", "webbook", "test-project")
    tracker.update("generating text", 0, 1)
    tracker.fail("Something went wrong")

    row = pdb.query(PipelineProgress).filter(PipelineProgress.id == tracker.progress_id).first()
    assert row.status == "error"
    assert row.error_message == "Something went wrong"


def test_progress_persists_across_queries(pdb):
    """Progress can be queried multiple times with consistent results."""
    tracker = ProgressTracker(pdb, "ep-2", "pdf_render", "test-project")
    tracker.update("rendering", 0, 1)

    row1 = (
        pdb.query(PipelineProgress)
        .filter(PipelineProgress.episode_id == "ep-2", PipelineProgress.operation == "pdf_render")
        .first()
    )
    assert row1 is not None
    assert row1.status == "running"

    tracker.complete()

    row2 = (
        pdb.query(PipelineProgress)
        .filter(PipelineProgress.episode_id == "ep-2", PipelineProgress.operation == "pdf_render")
        .first()
    )
    assert row2.status == "completed"


def test_multiple_operations_per_episode(pdb):
    """Multiple operations for the same episode are tracked independently."""
    t1 = ProgressTracker(pdb, "ep-3", "analysis", "test-project")
    t1.complete()

    t2 = ProgressTracker(pdb, "ep-3", "image_generation", "test-project")
    t2.update("generating", 3, 10)

    rows = pdb.query(PipelineProgress).filter(PipelineProgress.episode_id == "ep-3").all()
    assert len(rows) == 2

    analysis = [r for r in rows if r.operation == "analysis"][0]
    img_gen = [r for r in rows if r.operation == "image_generation"][0]
    assert analysis.status == "completed"
    assert img_gen.status == "running"
    assert img_gen.completed_steps == 3


# ------------------------------------------------------------------
# API integration test -- GET progress endpoint
# ------------------------------------------------------------------


@pytest.fixture(autouse=True)
def _setup_db():
    Base.metadata.create_all(engine)
    with TestClient(app):
        pass
    yield
    safe_drop_all(engine, Base.metadata)
    proj_dir = Path(settings.projects_dir)
    if proj_dir.exists():
        safe_rmtree(proj_dir)


@pytest.fixture()
def client():
    with TestClient(app, raise_server_exceptions=False) as c:
        yield c


def _admin_login(client: TestClient):
    resp = client.post("/api/v1/auth/login", json={"username": "admin", "password": "admin123"})
    assert resp.status_code == 200


def _create_project(client: TestClient) -> str:
    _admin_login(client)
    resp = client.post("/api/v1/projects/", json={"name": "Progress Test Proj"})
    assert resp.status_code == 200
    return resp.json()["id"]


def _create_episode(client: TestClient, project_id: str) -> str:
    pdf_bytes = make_test_pdf("INT. OFFICE - DAY")
    resp = client.post(
        f"/api/v1/projects/{project_id}/episodes/",
        data={"episode_number": "1", "title": "Pilot"},
        files={"file": ("pilot.pdf", io.BytesIO(pdf_bytes), "application/pdf")},
    )
    assert resp.status_code == 200
    return resp.json()["id"]


def test_progress_endpoint_empty(client: TestClient):
    """Progress endpoint returns nulls when no operations have run."""
    project_id = _create_project(client)
    episode_id = _create_episode(client, project_id)

    resp = client.get(f"/api/v1/projects/{project_id}/episodes/{episode_id}/progress")
    assert resp.status_code == 200
    data = resp.json()
    assert data["analysis"] is None
    assert data["image_generation"] is None
    assert data["webbook"] is None
    assert data["pdf_render"] is None


def test_progress_endpoint_with_data(client: TestClient):
    """Progress endpoint returns data after creating progress rows."""
    project_id = _create_project(client)
    episode_id = _create_episode(client, project_id)

    # Manually insert progress rows via single DB
    db = SessionLocal()
    tracker = ProgressTracker(db, episode_id, "analysis", project_id)
    tracker.update("엔티티 추출 중", 1, 2)
    tracker.complete()
    db.close()

    resp = client.get(f"/api/v1/projects/{project_id}/episodes/{episode_id}/progress")
    assert resp.status_code == 200
    data = resp.json()
    assert data["analysis"] is not None
    assert data["analysis"]["status"] == "completed"
    assert data["analysis"]["completed_steps"] == 1
    assert data["analysis"]["total_steps"] == 2
    assert data["image_generation"] is None
