"""Indexing CLI — scan, extract metadata, transcribe, and analyze video files."""

import argparse
import logging
import sys
from dataclasses import asdict, dataclass, field
from pathlib import Path
from typing import Callable, Optional

from . import DEFAULT_DB_PATH, __version__
from .db import Database
from .metadata import extract_metadata, scan_directory, FFProbeError

logger = logging.getLogger(__name__)

# Status progression: pending → metadata → transcribed → vision_done → complete
STATUS_ORDER = ["pending", "metadata", "transcribed", "vision_done", "complete"]


@dataclass
class IndexFileResult:
    """Result of indexing a single file."""
    clip_id: Optional[int] = None
    file_name: str = ""
    status: str = "error"  # "indexed", "skipped", "error"
    index_status: str = "pending"
    error: Optional[str] = None
    transcript_segments: int = 0
    frame_descriptions: int = 0


def _status_index(status: str) -> int:
    try:
        return STATUS_ORDER.index(status)
    except ValueError:
        return -1


def _auto_tags(meta, file_path: Path) -> list[str]:
    """Generate automatic tags from metadata."""
    tags = []

    # Resolution tags
    if meta.height:
        if meta.height >= 2160:
            tags.append("4k")
        elif meta.height >= 1080:
            tags.append("1080p")
        elif meta.height >= 720:
            tags.append("720p")
        elif meta.height >= 480:
            tags.append("480p")

    # Codec tag
    if meta.codec:
        tags.append(meta.codec.lower())

    # Directory name as tag
    parent = file_path.parent.name
    if parent and parent != ".":
        tags.append(parent.lower().replace(" ", "-"))

    return tags


def get_transcriber(model_name: str = "base"):
    """Create a Transcriber instance (lazy-imports whisper)."""
    from .transcribe import Transcriber
    return Transcriber(model_name=model_name)


def index_file(
    db: Database,
    file_path: Path,
    *,
    do_transcribe: bool = False,
    whisper_model: str = "base",
    skip_if_done: bool = False,
    transcriber=None,
    on_progress: Optional[Callable[[str], None]] = None,
) -> IndexFileResult:
    """Index a single video file through the pipeline.

    Args:
        db: Database instance.
        file_path: Path to the video file.
        do_transcribe: Run Whisper transcription.
        whisper_model: Whisper model name.
        skip_if_done: Skip if already indexed to target status.
        transcriber: Reusable Transcriber instance (created if None and needed).
        on_progress: Callback for progress messages.

    Returns:
        IndexFileResult with outcome details.
    """
    file_path = Path(file_path).resolve()
    result = IndexFileResult(file_name=file_path.name)

    def _progress(msg: str):
        if on_progress:
            on_progress(msg)

    # Determine target status
    if do_transcribe:
        target_status = "transcribed"
    else:
        target_status = "metadata"

    file_str = str(file_path)

    # Check skip
    if skip_if_done:
        existing = db.get_clip_by_path(file_str)
        if existing and _status_index(existing["index_status"]) >= _status_index(target_status):
            result.status = "skipped"
            result.clip_id = existing["id"]
            result.index_status = existing["index_status"]
            return result

    # ── Stage 1: Metadata ──
    _progress(f"Extracting metadata: {file_path.name}")
    try:
        meta = extract_metadata(file_path)
        meta_dict = asdict(meta)
        meta_dict["index_status"] = "metadata"
        clip_id = db.upsert_clip(**meta_dict)
        result.clip_id = clip_id
        result.index_status = "metadata"

        for tag in _auto_tags(meta, file_path):
            db.add_tag(clip_id, tag)

    except FFProbeError as e:
        result.error = str(e)
        return result
    except Exception as e:
        result.error = str(e)
        return result

    # ── Stage 2: Transcription ──
    if do_transcribe:
        _progress(f"Transcribing: {file_path.name}")
        try:
            if transcriber is None:
                transcriber = get_transcriber(whisper_model)

            segments = transcriber.transcribe(file_path)
            seg_dicts = [{"start": s.start, "end": s.end, "text": s.text} for s in segments]
            db.insert_transcript_segments(clip_id, seg_dicts)
            db.update_clip_status(clip_id, "transcribed")
            result.index_status = "transcribed"
            result.transcript_segments = len(segments)

        except Exception as e:
            logger.error("  Transcription failed: %s", e)
            db.update_clip_status(clip_id, "metadata", str(e))
            result.error = f"Transcription failed: {e}"
            result.status = "indexed"  # metadata succeeded
            return result

    result.status = "indexed"
    return result


def run_pipeline(args):
    """Run the indexing pipeline."""
    db = Database(args.db)

    # Determine what stages to run
    do_transcribe = not args.metadata_only

    # Collect all video files from given paths
    all_files = []
    for path_str in args.paths:
        path = Path(path_str).resolve()
        if path.is_file():
            all_files.append(path)
        elif path.is_dir():
            all_files.extend(scan_directory(path, recursive=True))
        else:
            logger.warning("Path not found: %s", path)

    if not all_files:
        logger.error("No video files found.")
        db.close()
        return 1

    logger.info("Found %d video file(s) to process", len(all_files))

    stats = {"processed": 0, "skipped": 0, "errors": 0}
    transcriber = None

    for file_path in all_files:
        logger.info("Processing: %s", file_path.name)

        result = index_file(
            db,
            file_path,
            do_transcribe=do_transcribe,
            whisper_model=args.whisper_model,
            skip_if_done=args.new_only,
            transcriber=transcriber,
        )

        if result.status == "skipped":
            logger.info("  Skipping (already indexed)")
            stats["skipped"] += 1
        elif result.status == "error" and result.clip_id is None:
            logger.error("  Error: %s", result.error)
            stats["errors"] += 1
        else:
            logger.info("  Indexed (clip_id=%s, status=%s)", result.clip_id, result.index_status)
            stats["processed"] += 1

        # Reuse transcriber for next file (lazy-created inside index_file if needed)
        if transcriber is None and do_transcribe:
            try:
                transcriber = get_transcriber(args.whisper_model)
            except Exception:
                pass

    # Summary
    counts = db.get_clip_count()
    db.close()

    logger.info("─── Indexing complete ───")
    logger.info("Processed: %d | Skipped: %d | Errors: %d", stats["processed"], stats["skipped"], stats["errors"])
    logger.info("Database: %s", args.db)
    logger.info("Clips by status: %s", counts)

    return 0


def main():
    parser = argparse.ArgumentParser(
        prog="takeseek-index",
        description="Index video files into the TakeSeek database.",
    )
    parser.add_argument("paths", nargs="+", help="Video files or directories to index")
    parser.add_argument("--db", default=DEFAULT_DB_PATH, help=f"Database path (default: {DEFAULT_DB_PATH})")
    parser.add_argument("--metadata-only", action="store_true", help="Only extract metadata (no transcription)")
    parser.add_argument("--new-only", action="store_true", help="Skip files already indexed to target status")
    parser.add_argument("--whisper-model", default="base", help="Whisper model name (default: base)")
    parser.add_argument("--verbose", "-v", action="store_true", help="Enable verbose logging")
    parser.add_argument("--version", action="version", version=f"%(prog)s {__version__}")

    args = parser.parse_args()

    level = logging.DEBUG if args.verbose else logging.INFO
    logging.basicConfig(
        level=level,
        format="%(asctime)s [%(levelname)s] %(message)s",
        datefmt="%H:%M:%S",
    )

    sys.exit(run_pipeline(args))


if __name__ == "__main__":
    main()
