"""File discovery for coding agent session files."""

from __future__ import annotations

import glob
import os
from dataclasses import dataclass
from pathlib import Path

from qc_trace.daemon.config import DaemonConfig
from qc_trace.daemon.state import FileState, StateManager
from qc_trace.schemas.unified import SourceType


@dataclass
class ChangedFile:
    """A file that has changed since last processing."""

    path: str
    source: SourceType
    mtime: float
    size: int


class FileWatcher:
    """Discovers session files and detects changes."""

    def __init__(self, config: DaemonConfig, state_mgr: StateManager) -> None:
        self._config = config
        self._state_mgr = state_mgr
        self._home = str(Path.home())

    def get_changed_files(self) -> list[ChangedFile]:
        """Scan all source directories and return files that changed since last check."""
        changed: list[ChangedFile] = []
        source_globs: list[tuple[SourceType, str]] = [
            ("claude_code", self._config.claude_code_glob),
            ("codex_cli", self._config.codex_cli_glob),
            ("gemini_cli", self._config.gemini_cli_glob),
            ("cursor", self._config.cursor_glob),
        ]

        for source, pattern in source_globs:
            full_pattern = os.path.join(self._home, pattern)
            for file_path in glob.glob(full_pattern, recursive=True):
                cf = self._check_file(file_path, source)
                if cf is not None:
                    changed.append(cf)

        return changed

    def _check_file(self, file_path: str, source: SourceType) -> ChangedFile | None:
        """Check if a single file has changed and is eligible for processing."""
        try:
            stat = os.stat(file_path)
        except OSError:
            return None

        size = stat.st_size
        mtime = stat.st_mtime

        # Skip files over max size
        if size > self._config.max_file_size:
            return None

        # Skip empty files
        if size == 0:
            return None

        existing = self._state_mgr.get_state(file_path)
        if existing is not None:
            # Skip if file hasn't changed
            if existing.last_mtime == mtime and existing.last_size == size:
                return None
            # Skip if max retries exceeded (until file changes again)
            if (
                existing.retry_count >= self._config.max_retries_per_file
                and existing.last_mtime == mtime
            ):
                return None

        return ChangedFile(path=file_path, source=source, mtime=mtime, size=size)
