"""ImageUploadService — 사용자 이미지 업로드 서비스 (W5 F23 Phase 4).

ImageService facade(image_service.py)에서 upload_custom_image를
literal lift. facade는 얇은 delegate만 유지.
"""

import uuid
from datetime import datetime, timezone
from pathlib import Path
from typing import Any, Dict, Optional

from sqlalchemy.orm import Session as OrmSession

from app.core.config import settings
from app.core.errors import AppError
from app.core.file_paths import to_relative_image_path
from app.i18n.loader import t
from app.logging.activity_logger import ActivityLogger
from app.models.project import EntityCanon, EntityEpisodeLink, ImageAsset, SceneStill
from app.services.image_service_helpers import (
    MANUAL_UPLOAD_PROMPT,
    auto_set_primary,
    image_to_dict,
)


def _now() -> str:
    return datetime.now(timezone.utc).isoformat()


def _new_id() -> str:
    return str(uuid.uuid4())


class ImageUploadService:
    """사용자 이미지 업로드 서비스."""

    def __init__(
        self,
        db: OrmSession,
        project_id: str,
        actor_id: str,
        activity_logger: ActivityLogger,
    ) -> None:
        self._db = db
        self._project_id = project_id
        self._actor_id = actor_id
        self._logger = activity_logger

    def upload_custom_image(
        self,
        file_bytes: bytes,
        filename: str,
        entity_id: Optional[str] = None,
        still_id: Optional[str] = None,
        episode_id: Optional[str] = None,
        ip: Optional[str] = None,
    ) -> Dict[str, Any]:
        """Upload a custom image file and create an ImageAsset record."""
        # Validate ownership: entity/still must belong to this project
        if entity_id:
            owner_check = (
                self._db.query(EntityCanon)
                .filter(EntityCanon.id == entity_id, EntityCanon.project_id == self._project_id)
                .first()
            )
            if not owner_check:
                raise AppError(
                    code="image.entity_not_found",
                    message=t("image.not_found"),
                    status_code=404,
                )
        if still_id:
            owner_check = (
                self._db.query(SceneStill)
                .filter(SceneStill.id == still_id, SceneStill.project_id == self._project_id)
                .first()
            )
            if not owner_check:
                raise AppError(
                    code="image.still_not_found",
                    message=t("image.not_found"),
                    status_code=404,
                )

        # Determine asset type
        asset_type = "reference" if entity_id else "scene"

        # Determine episode_id from entity or still if not provided
        if not episode_id:
            if entity_id:
                link = (
                    self._db.query(EntityEpisodeLink)
                    .filter(EntityEpisodeLink.canon_id == entity_id)
                    .first()
                )
                episode_id = link.episode_id if link else "shared"
            elif still_id:
                still = (
                    self._db.query(SceneStill)
                    .filter(SceneStill.id == still_id)
                    .first()
                )
                episode_id = still.episode_id if still else "shared"
            else:
                episode_id = "shared"

        # Save file
        project_dir = Path(settings.projects_dir) / self._project_id
        sub_dir = "reference" if entity_id else "scene"
        images_dir = project_dir / "images" / episode_id / sub_dir
        images_dir.mkdir(parents=True, exist_ok=True)

        image_id = _new_id()
        ext = Path(filename).suffix or ".png"
        file_path = images_dir / f"{image_id}{ext}"
        file_path.write_bytes(file_bytes)

        asset = ImageAsset(
            id=image_id,
            project_id=self._project_id,
            asset_type=asset_type,
            entity_id=entity_id,
            still_id=still_id,
            episode_id=episode_id,
            file_path=to_relative_image_path(file_path),
            # ★표식은 한 자리에서 정한다 — `auto_set_primary` 가 이 값으로
            #  「사람이 올린 대표」를 알아본다(2026-09-20).
            prompt_used=MANUAL_UPLOAD_PROMPT,
            generation_model="manual_upload",
            width=None,
            height=None,
            status="generated",
            review_notes="",
            created_at=_now(),
        )
        self._db.add(asset)
        auto_set_primary(self._db, self._project_id, asset)
        self._db.commit()

        self._logger.log(
            actor_id=self._actor_id,
            action="image.upload",
            resource_type="image",
            resource_id=image_id,
            project_id=self._project_id,
            detail={"entity_id": entity_id, "still_id": still_id, "filename": filename},
            ip_address=ip,
        )

        return image_to_dict(asset)
