"""Track importer for AI MusiMuse.

This module implements :class:`TrackImporter`, which synchronizes a
:class:`~scanner.manifest.TrackManifestCollection` with the database.

The Importer:

- creates a default project if none exists
- imports new tracks
- detects duplicate hashes
- detects modified files (reports without overwriting)
- detects deleted tracks (marks as MISSING)

The Importer must never scan directories directly.  It consumes
TrackManifestCollection only and communicates with the database
exclusively through the repository layer.
"""

from __future__ import annotations

from dataclasses import dataclass

from sqlalchemy.orm import Session

from database.models import Track
from database.repositories import ProjectRepository, TrackRepository
from log.logger import get_logger
from scanner.manifest import TrackManifestCollection

_logger = get_logger(__name__)


@dataclass(frozen=True)
class ImportResult:
    """Summary of an import operation.

    Attributes:
        new_tracks: Number of new tracks imported.
        duplicates: Number of duplicate files detected (same hash).
        modified: Number of modified files detected (path exists, hash differs).
        deleted: Number of tracks marked as MISSING.
        unchanged: Number of tracks unchanged.
    """

    new_tracks: int
    duplicates: int
    modified: int
    deleted: int
    unchanged: int


class TrackImporter:
    """Synchronizes a TrackManifestCollection with the database.

    The importer consumes a manifest collection and uses repositories
    to persist changes.  It never accesses the filesystem directly.
    """

    @staticmethod
    def import_manifests(
        session: Session,
        collection: TrackManifestCollection,
    ) -> ImportResult:
        """Synchronize the manifest collection with the database.

        Args:
            session: An active SQLAlchemy session.
            collection: The manifest collection to import.

        Returns:
            An :class:`ImportResult` summarizing the operation.
        """
        project = ProjectRepository.get_default_project(session)

        existing_tracks = TrackRepository.list_tracks(session, project.id)
        existing_by_path: dict[str, Track] = {
            t.relative_path: t for t in existing_tracks
        }

        new_tracks = 0
        duplicates = 0
        modified = 0
        unchanged = 0

        for manifest in collection:
            existing = existing_by_path.get(manifest.relative_path)

            if existing is None:
                hash_matches = TrackRepository.find_by_sha256(session, manifest.sha256)
                if hash_matches:
                    duplicates += 1
                    _logger.warning(
                        "duplicate.detected",
                        path=manifest.relative_path,
                        sha256=manifest.sha256,
                        existing_path=hash_matches[0].relative_path,
                    )
                else:
                    TrackRepository.create_track(
                        session,
                        project_id=project.id,
                        relative_path=manifest.relative_path,
                        original_filename=manifest.original_filename,
                        sha256=manifest.sha256,
                        file_size=manifest.file_size,
                    )
                    new_tracks += 1
                    _logger.info(
                        "track.imported",
                        path=manifest.relative_path,
                        sha256=manifest.sha256,
                    )
            else:
                if existing.sha256 == manifest.sha256:
                    unchanged += 1
                else:
                    modified += 1
                    _logger.warning(
                        "modified.detected",
                        path=manifest.relative_path,
                        old_sha256=existing.sha256,
                        new_sha256=manifest.sha256,
                    )

        manifest_paths = collection.paths
        deleted = 0
        for track in existing_tracks:
            if track.relative_path not in manifest_paths:
                TrackRepository.mark_as_missing(session, track)
                deleted += 1
                _logger.info(
                    "deleted.detected",
                    path=track.relative_path,
                )

        session.commit()

        _logger.info(
            "import.complete",
            new=new_tracks,
            duplicates=duplicates,
            modified=modified,
            deleted=deleted,
            unchanged=unchanged,
        )

        return ImportResult(
            new_tracks=new_tracks,
            duplicates=duplicates,
            modified=modified,
            deleted=deleted,
            unchanged=unchanged,
        )
