from pathlib import Path
from typing import Optional
from fastapi import UploadFile, HTTPException
from sqlmodel import Session
from uuid import uuid4
import json

from app.models.product_template import ProductTemplate
from app.schemas.product_template import ProductTemplateRead
from app.schemas.common import PaginatedResponse
from app.repositories.product_template_repo import ProductTemplateRepository
from app.utils.image import validate_base_file, save_upload_file_streamed, safe_delete
from app.utils.psd_shared import generate_psd_preview
from app.utils.psd_ratio import extract_psd_ratio
from app.core.logging import setup_logger
from app.core.config import get_settings
from app.core.constants import ERR_NOT_FOUND, ERR_FILE_INVALID

logger = setup_logger(__name__)
settings = get_settings()


class ProductTemplateService:

    @staticmethod
    async def upload_template(
        name: str,
        psd_file: UploadFile,
        user_id: int,
        session: Session,
    ) -> ProductTemplate:

        validate_base_file(psd_file)

        store_dir = Path(settings.PSD_FILES_DIRECTORY)
        store_dir.mkdir(parents=True, exist_ok=True)

        # Save PSD
        try:
            file_path = await save_upload_file_streamed(psd_file, store_dir)
        except Exception as exc:
            logger.exception("Failed to save PSD file")
            raise HTTPException(status_code=500, detail=ERR_FILE_INVALID)

        # Preview
        preview_path = store_dir / f"{uuid4().hex}_preview.png"
        preview_result = generate_psd_preview(file_path, preview_path)
        preview_rel = str(preview_result) if preview_result else None

        # Ratio
        ratio = extract_psd_ratio(file_path)

        # DB Record
        tpl = ProductTemplate(
            name=name,
            uploaded_by=user_id,
            file_path=str(file_path),
            preview_png_path=preview_rel,
            ratio=ratio
        )

        tpl = ProductTemplateRepository.create(session, tpl)
        return tpl

    @staticmethod
    async def update_template(
        template_id: int,
        name: Optional[str],
        new_psd: Optional[UploadFile],
        session: Session,
    ) -> ProductTemplate:

        tpl = ProductTemplateRepository.get_by_id(session, template_id)
        if not tpl or tpl.is_deleted:
            raise HTTPException(status_code=404, detail=ERR_NOT_FOUND)

        if name:
            tpl.name = name

        if new_psd:
            validate_base_file(new_psd)

            store_dir = Path(settings.PSD_FILES_DIRECTORY)
            store_dir.mkdir(parents=True, exist_ok=True)

            try:
                new_path = await save_upload_file_streamed(new_psd, store_dir)
            except Exception:
                raise HTTPException(status_code=500, detail=ERR_FILE_INVALID)

            # Preview
            preview_path = store_dir / f"{uuid4().hex}_preview.png"
            preview_result = generate_psd_preview(new_path, preview_path)
            preview_rel = str(preview_result) if preview_result else None

            # Ratio
            ratio = extract_psd_ratio(new_path)

            # Clean old files
            safe_delete(Path(tpl.file_path))
            if tpl.preview_png_path:
                safe_delete(Path(tpl.preview_png_path))

            tpl.file_path = str(new_path)
            tpl.preview_png_path = preview_rel
            tpl.ratio = ratio

        tpl = ProductTemplateRepository.update(session, tpl)
        return tpl

    @staticmethod
    def list_templates(
        session: Session,
        page: int,
        limit: int,
        search: Optional[str],
        sort_by: Optional[str],
        order: Optional[str],
    ):
        """Return all active (not deleted) templates."""
        items, total = ProductTemplateRepository.list_advanced(
            session=session,
            page=page,
            limit=limit,
            search=search,
            sort_by=sort_by,
            order=order,
        )

        return PaginatedResponse[ProductTemplateRead](
            total=total,
            page=page,
            limit=limit,
            items=items,
        )

    @staticmethod
    def get_template_by_id(template_id: int, session: Session) -> ProductTemplate:
        """
        Retrieve a template ensuring it exists and is not deleted.
        """
        tpl = ProductTemplateRepository.get_by_id(session, template_id)
        if not tpl or tpl.is_deleted:
            raise HTTPException(status_code=404, detail=ERR_NOT_FOUND)

        return tpl

    @staticmethod
    def delete_template(template_id: int, session: Session):
        """Soft-delete template and remove files (non-critical)."""
        tpl = ProductTemplateRepository.get_by_id(session, template_id)
        if not tpl:
            raise HTTPException(status_code=404, detail=ERR_NOT_FOUND)

        ProductTemplateRepository.soft_delete_template(session, tpl)

        # best-effort deletes
        safe_delete(Path(tpl.file_path))
        if tpl.preview_png_path:
            safe_delete(Path(tpl.preview_png_path))
