# FILE: src/apps/pages/services/page_content_service.py
from __future__ import annotations

import asyncio
import io
import uuid as _uuid

from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession

from src.apps.pages.models.page_content import TenantPageContent
from src.core.config import settings
from src.core.exceptions import ValidationError


class PageContentService:
    @staticmethod
    async def get(db: AsyncSession, tenant_id, page_slug: str) -> TenantPageContent | None:
        result = await db.execute(
            select(TenantPageContent).where(
                TenantPageContent.tenant_id == tenant_id,
                TenantPageContent.page_slug == page_slug,
            )
        )
        return result.scalar_one_or_none()

    @staticmethod
    async def upsert(
        db: AsyncSession, tenant_id, page_slug: str, content: dict, updated_by
    ) -> TenantPageContent:
        row = await PageContentService.get(db, tenant_id, page_slug)
        if row is None:
            row = TenantPageContent(
                tenant_id=tenant_id, page_slug=page_slug, content=content, updated_by=updated_by
            )
            db.add(row)
        else:
            row.content = content
            row.updated_by = updated_by
        await db.flush()
        await db.refresh(row)
        return row

    @staticmethod
    def _detect_image_mime(content: bytes) -> str | None:
        """Validate by magic bytes, not just the client-supplied Content-Type header."""
        if content.startswith(b"\xff\xd8\xff"):
            return "image/jpeg"
        if content.startswith(b"\x89PNG\r\n\x1a\n"):
            return "image/png"
        if content[:4] == b"RIFF" and content[8:12] == b"WEBP":
            return "image/webp"
        return None

    @staticmethod
    async def upload_map_image(tenant_id, content: bytes, filename: str) -> str:
        mime = PageContentService._detect_image_mime(content)
        if mime is None:
            raise ValidationError("Image must be JPG, PNG, or WebP and under 5 MB")
        if len(content) > 5 * 1024 * 1024:
            raise ValidationError("Image must be JPG, PNG, or WebP and under 5 MB")

        # Strip EXIF metadata before storing (PRD A08)
        try:
            from PIL import Image

            img = Image.open(io.BytesIO(content))
            clean = io.BytesIO()
            img_format = img.format or "JPEG"
            data = list(img.getdata())
            stripped = Image.new(img.mode, img.size)
            stripped.putdata(data)
            stripped.save(clean, format=img_format)
            content = clean.getvalue()
        except Exception as exc:
            raise ValidationError(f"Could not process image: {exc}") from exc

        ext = {"image/jpeg": "jpg", "image/png": "png", "image/webp": "webp"}[mime]
        key = f"tenant/{tenant_id}/pages/map/{_uuid.uuid4()}.{ext}"
        await PageContentService._put_s3_object(key, content, mime)
        return key

    @staticmethod
    async def upload_map_pdf(tenant_id, content: bytes, lang: str) -> str:
        if not content.startswith(b"%PDF-"):
            raise ValidationError("PDF must be under 10 MB")
        if len(content) > 10 * 1024 * 1024:
            raise ValidationError("PDF must be under 10 MB")

        key = f"tenant/{tenant_id}/pages/map/map-{lang}-{_uuid.uuid4()}.pdf"
        await PageContentService._put_s3_object(key, content, "application/pdf")
        return key

    @staticmethod
    async def _put_s3_object(key: str, content: bytes, mime: str) -> None:
        import boto3
        import botocore.exceptions

        if not settings.AWS_ACCESS_KEY_ID or not settings.S3_DOCUMENTS_BUCKET:
            raise ValidationError("S3 storage is not configured.")

        s3 = boto3.client(
            "s3",
            aws_access_key_id=settings.AWS_ACCESS_KEY_ID,
            aws_secret_access_key=settings.AWS_SECRET_ACCESS_KEY,
            region_name=settings.S3_DOCUMENTS_REGION or "us-east-1",
        )
        try:
            await asyncio.to_thread(
                lambda: s3.put_object(
                    Bucket=settings.S3_DOCUMENTS_BUCKET,
                    Key=key,
                    Body=content,
                    ContentType=mime,
                )
            )
        except botocore.exceptions.ClientError as exc:
            raise ValidationError(f"File upload failed: {exc}") from exc
