from pathlib import Path
import re

from .context import AppContext
from .io_ops import (
    apply_subtitle_badge,
    build_output_dir,
    download_cover,
    link_video_reference,
    move_failed_video,
    move_video,
    write_nfo,
)
from .models import MovieMetadata
from .numbering import extract_number
from .sources import build_scrapers


class CapturePipeline:
    def __init__(self, ctx: AppContext):
        self.ctx = ctx
        self.scrapers = build_scrapers()

    def run(self, target: Path) -> int:
        videos = self._collect_videos(target)
        if not videos:
            print("[-] No video files found.")
            return 1

        failed = 0
        success_dirs: list[Path] = []
        failed_files: list[Path] = []
        for video in videos:
            ok, out_dir, fail_path = self._handle_one(video)
            if not ok:
                failed += 1
                failed_files.append(fail_path or video)
                continue
            if out_dir is not None:
                success_dirs.append(out_dir)

        print("=" * 64)
        print(f"[+] Finished. success={len(videos) - failed}, failed={failed}")
        if success_dirs:
            print("[+] Output paths:")
            for p in success_dirs:
                print(f"    {p.resolve()}")
        if failed_files:
            print("[-] Failed file paths:")
            for p in failed_files:
                print(f"    {p.resolve()}")
        return 0 if failed == 0 else 2

    def _collect_videos(self, target: Path) -> list[Path]:
        allowed = {".mp4", ".avi", ".rmvb", ".wmv", ".mov", ".mkv", ".flv", ".ts", ".webm"}
        escape_folders = set(self.ctx.config.escape_folders())

        if target.is_file():
            return [target] if target.suffix.lower() in allowed else []

        if not target.is_dir():
            return []

        videos: list[Path] = []
        for p in target.rglob("*"):
            if not p.is_file() or p.suffix.lower() not in allowed:
                continue
            if any(part in escape_folders for part in p.parts):
                continue
            videos.append(p)
        return videos

    def _handle_one(self, video: Path) -> tuple[bool, Path | None, Path | None]:
        print(f"[!] Processing: {video}")
        try:
            number = extract_number(str(video))
            meta = self._fetch(number)
            if not meta:
                print(f"[-] Metadata not found: {number}")
                return self._on_failure(video, reason=f"Metadata not found: {number}")

            has_subtitle = self._has_subtitle(video)
            output_number = f"{meta.number}-C" if has_subtitle else meta.number
            out_dir = build_output_dir(self.ctx, meta.actor, output_number)
            if meta.cover:
                _fanart, poster = download_cover(
                    self.ctx,
                    meta.cover,
                    out_dir,
                    output_number,
                    referer_url=meta.website,
                )
                if has_subtitle and self.ctx.config.subtitle_badge_enabled():
                    apply_subtitle_badge(
                        self.ctx,
                        poster,
                        backup_enabled=self.ctx.config.subtitle_badge_backup_enabled(),
                    )
            write_nfo(self.ctx, meta, out_dir, has_subtitle=has_subtitle)
            if self.ctx.debug:
                link_video_reference(video, out_dir, output_number)
            else:
                move_video(video, out_dir, output_number)
            print(f"[+] Done: {output_number} ({meta.source})")
            return True, out_dir, None
        except Exception as err:
            return self._on_failure(video, reason=str(err))

    def _on_failure(self, video: Path, reason: str) -> tuple[bool, None, Path]:
        print(f"[-] Failed: {video} -> {reason}")
        if self.ctx.config.failed_move_enabled():
            try:
                moved = move_failed_video(self.ctx, video)
                print(f"[-] Failed file moved: {moved.resolve()}")
                return False, None, moved
            except Exception as move_err:
                print(f"[-] Failed to move failed file: {video} -> {move_err}")
        return False, None, video

    def _fetch(self, number: str) -> MovieMetadata | None:
        for scraper in self.scrapers:
            data = scraper.fetch(number, self.ctx)
            if data and data.is_valid():
                return data
        return None

    @staticmethod
    def _has_subtitle(video: Path) -> bool:
        stem = video.stem.lower()
        compact = re.sub(r"[\s._-]+", "", stem)

        # Match compact forms like ABC123C / ABC123CHS / ABC-123C (after normalization).
        if re.search(r"[a-z]{2,10}\d{2,6}(?:c|ch|chs|cht)$", compact):
            return True

        # Match separated suffix forms like xxx-c / xxx_ch / xxx.cht
        if re.search(r"(?:^|[-_.\s])(c|ch|chs|cht)(?:$|[-_.\s])", stem):
            return True

        return False
