"""Photo import and management services."""

from __future__ import annotations

import os
from pathlib import Path
from datetime import datetime, date
from typing import Callable, Optional, Tuple

from PIL import Image
from sqlalchemy.orm import Session

from src.core.config import SUPPORTED_IMAGE_FORMATS
from src.web.db.models import Photo


def extract_exif_date(image_path: str) -> Optional[date]:
    """Extract date taken from photo EXIF data - returns Date (not DateTime) to match desktop schema.
    
    Tries multiple methods to extract EXIF date:
    1. PIL's getexif() (modern method)
    2. PIL's _getexif() (deprecated but sometimes more reliable)
    3. Access EXIF IFD directly if available
    """
    try:
        with Image.open(image_path) as image:
            exifdata = None
            
            # Try modern getexif() first
            try:
                exifdata = image.getexif()
            except Exception:
                pass
            
            # If getexif() didn't work or returned empty, try deprecated _getexif()
            if not exifdata or len(exifdata) == 0:
                try:
                    if hasattr(image, '_getexif'):
                        exifdata = image._getexif()
                except Exception:
                    pass
            
            if not exifdata:
                return None
            
            # Look for date taken in EXIF tags
            # Priority: DateTimeOriginal (when photo was taken) > DateTimeDigitized > DateTime (file modification)
            date_tags = [
                36867,  # DateTimeOriginal - when photo was actually taken (highest priority)
                36868,  # DateTimeDigitized - when photo was digitized
                306,  # DateTime - file modification date (lowest priority)
            ]
            
            # Try direct access first
            for tag_id in date_tags:
                try:
                    if tag_id in exifdata:
                        date_str = exifdata[tag_id]
                        if date_str:
                            # Parse EXIF date format (YYYY:MM:DD HH:MM:SS)
                            try:
                                dt = datetime.strptime(date_str, "%Y:%m:%d %H:%M:%S")
                                return dt.date()
                            except ValueError:
                                # Try alternative format
                                try:
                                    dt = datetime.strptime(date_str, "%Y-%m-%d %H:%M:%S")
                                    return dt.date()
                                except ValueError:
                                    continue
                except (KeyError, TypeError):
                    continue
            
            # Try accessing EXIF IFD directly if available (for tags in EXIF IFD like DateTimeOriginal)
            try:
                if hasattr(exifdata, 'get_ifd'):
                    # EXIF IFD is at offset 0x8769
                    exif_ifd = exifdata.get_ifd(0x8769)
                    if exif_ifd:
                        for tag_id in date_tags:
                            if tag_id in exif_ifd:
                                date_str = exif_ifd[tag_id]
                                if date_str:
                                    try:
                                        dt = datetime.strptime(date_str, "%Y:%m:%d %H:%M:%S")
                                        return dt.date()
                                    except ValueError:
                                        try:
                                            dt = datetime.strptime(date_str, "%Y-%m-%d %H:%M:%S")
                                            return dt.date()
                                        except ValueError:
                                            continue
            except Exception:
                pass
            
    except Exception as e:
        # Log error for debugging (but don't fail the import)
        import logging
        logger = logging.getLogger(__name__)
        logger.debug(f"Failed to extract EXIF date from {image_path}: {e}")
    
    return None


def extract_photo_date(image_path: str) -> Optional[date]:
    """Extract date taken from photo with fallback to file modification time.
    
    Tries in order:
    1. EXIF date tags (DateTimeOriginal, DateTimeDigitized, DateTime)
    2. File modification time (as fallback)
    
    Returns:
        Date object or None if no date can be determined
    """
    # First try EXIF date extraction
    date_taken = extract_exif_date(image_path)
    if date_taken:
        return date_taken
    
    # Fallback to file modification time
    try:
        if os.path.exists(image_path):
            mtime = os.path.getmtime(image_path)
            mtime_date = datetime.fromtimestamp(mtime).date()
            return mtime_date
    except Exception as e:
        # Log error for debugging (but don't fail the import)
        import logging
        logger = logging.getLogger(__name__)
        logger.debug(f"Failed to get file modification time from {image_path}: {e}")
    
    return None


def find_photos_in_folder(folder_path: str, recursive: bool = True) -> list[str]:
    """Find all photo files in a folder."""
    folder_path = os.path.abspath(folder_path)
    if not os.path.isdir(folder_path):
        return []

    found_photos = []

    if recursive:
        for root, _dirs, files in os.walk(folder_path):
            for file in files:
                file_ext = Path(file).suffix.lower()
                if file_ext in SUPPORTED_IMAGE_FORMATS:
                    photo_path = os.path.join(root, file)
                    found_photos.append(photo_path)
    else:
        for file in os.listdir(folder_path):
            file_ext = Path(file).suffix.lower()
            if file_ext in SUPPORTED_IMAGE_FORMATS:
                photo_path = os.path.join(folder_path, file)
                if os.path.isfile(photo_path):
                    found_photos.append(photo_path)

    return found_photos


def import_photo_from_path(
    db: Session, photo_path: str, update_progress: Optional[Callable[[int, int, str], None]] = None
) -> Tuple[Optional[Photo], bool]:
    """Import a single photo from file path into database.

    Returns:
        Tuple of (Photo instance or None, is_new: bool)
    """
    photo_path = os.path.abspath(photo_path)
    filename = os.path.basename(photo_path)

    # Check if photo already exists by path
    existing = db.query(Photo).filter(Photo.path == photo_path).first()
    if existing:
        # If existing photo doesn't have date_taken, try to update it
        if existing.date_taken is None:
            date_taken = extract_photo_date(photo_path)
            if date_taken:
                existing.date_taken = date_taken
                db.commit()
                db.refresh(existing)
        return existing, False

    # Extract date taken with fallback to file modification time
    date_taken = extract_photo_date(photo_path)

    # Create new photo record - match desktop schema exactly
    # Desktop schema: id, path, filename, date_added, date_taken (DATE), processed
    photo = Photo(
        path=photo_path,
        filename=filename,
        date_taken=date_taken,
        processed=False,
    )

    db.add(photo)
    db.commit()
    db.refresh(photo)

    return photo, True


def import_photos_from_folder(
    db: Session,
    folder_path: str,
    recursive: bool = True,
    update_progress: Optional[Callable[[int, int, str], None]] = None,
) -> Tuple[int, int]:
    """Import all photos from a folder.

    Args:
        db: Database session
        folder_path: Path to folder to scan
        recursive: Whether to scan subdirectories
        update_progress: Optional callback(processed, total, current_file)

    Returns:
        Tuple of (added_count, existing_count)
    """
    found_photos = find_photos_in_folder(folder_path, recursive)
    total = len(found_photos)

    if total == 0:
        return 0, 0

    added_count = 0
    existing_count = 0

    for idx, photo_path in enumerate(found_photos, 1):
        try:
            photo, is_new = import_photo_from_path(db, photo_path)
            if is_new:
                added_count += 1
            else:
                existing_count += 1

            if update_progress:
                update_progress(idx, total, os.path.basename(photo_path))
        except Exception:
            # Log error but continue
            if update_progress:
                update_progress(idx, total, f"Error: {os.path.basename(photo_path)}")

    return added_count, existing_count

