"""Private job media and separately reviewed public gallery contracts."""
from typing import Literal
from pydantic import Field,model_validator,field_validator
from .models import Strict,Version
from .site_models import safe_slug

ID=r'^[a-f0-9]{32}$'
PHASE=Literal['general','before','after','progress','detail']
CATEGORY=r'^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$'

SCHEMA='''
CREATE TABLE IF NOT EXISTS photo_blobs(id TEXT PRIMARY KEY,sha256 TEXT NOT NULL,bytes INTEGER NOT NULL,width INTEGER NOT NULL,height INTEGER NOT NULL,created_at TEXT NOT NULL);
CREATE TABLE IF NOT EXISTS job_photos(
 id TEXT PRIMARY KEY,job_id TEXT NOT NULL REFERENCES jobs(id),customer_id TEXT NOT NULL REFERENCES customers(id),blob_id TEXT UNIQUE NOT NULL REFERENCES photo_blobs(id),
 uploaded_by TEXT NOT NULL,membership_id TEXT NOT NULL,caption TEXT NOT NULL DEFAULT '',description TEXT NOT NULL DEFAULT '',category TEXT NOT NULL DEFAULT 'general',phase TEXT NOT NULL DEFAULT 'general',pair_id TEXT NOT NULL DEFAULT '',position INTEGER NOT NULL DEFAULT 0,
 reviewed INTEGER NOT NULL DEFAULT 0 CHECK(reviewed IN (0,1)),customer_share TEXT,archived INTEGER NOT NULL DEFAULT 0 CHECK(archived IN (0,1)),version INTEGER NOT NULL DEFAULT 1,created_at TEXT NOT NULL,updated_at TEXT NOT NULL);
CREATE INDEX IF NOT EXISTS job_photos_job ON job_photos(job_id,archived,position);
CREATE UNIQUE INDEX IF NOT EXISTS photo_pair_phase ON job_photos(job_id,pair_id,phase) WHERE pair_id<>'' AND archived=0;
CREATE TABLE IF NOT EXISTS photo_history(id INTEGER PRIMARY KEY AUTOINCREMENT,photo_id TEXT NOT NULL REFERENCES job_photos(id),action TEXT NOT NULL,actor_id TEXT NOT NULL,membership_id TEXT NOT NULL,snapshot TEXT NOT NULL,created_at TEXT NOT NULL);
CREATE TABLE IF NOT EXISTS gallery_projects(id TEXT PRIMARY KEY,source_job_id TEXT UNIQUE REFERENCES jobs(id),draft_slug TEXT UNIQUE NOT NULL,draft TEXT NOT NULL,published TEXT,archived INTEGER NOT NULL DEFAULT 0 CHECK(archived IN (0,1)),version INTEGER NOT NULL DEFAULT 1,created_at TEXT NOT NULL,updated_at TEXT NOT NULL);
CREATE TABLE IF NOT EXISTS gallery_history(id INTEGER PRIMARY KEY AUTOINCREMENT,project_id TEXT NOT NULL REFERENCES gallery_projects(id),action TEXT NOT NULL,actor_id TEXT NOT NULL,membership_id TEXT NOT NULL,version INTEGER NOT NULL,snapshot TEXT NOT NULL,permission_confirmed INTEGER NOT NULL DEFAULT 0 CHECK(permission_confirmed IN (0,1)),created_at TEXT NOT NULL);
'''


class PhotoFields(Strict):
    caption:str=Field(default='',max_length=200)
    description:str=Field(default='',max_length=3000)
    category:str=Field(default='general',pattern=CATEGORY,max_length=60)
    phase:PHASE='general'
    pair_id:str=Field(default='',max_length=60,pattern=r'^(?:[a-z0-9]+(?:-[a-z0-9]+)*)?$')
    position:int=Field(default=0,strict=True,ge=0,le=9999)
    @model_validator(mode='after')
    def pairing(self):
        if self.pair_id and self.phase not in ('before','after'):raise ValueError('Only Before and After photos can belong to a comparison pair.')
        return self


class PhotoEdit(PhotoFields):
    version:int=Field(strict=True,ge=1)


class PhotoShare(Version):
    shared:bool=Field(strict=True)
    caption:str=Field(default='',max_length=200)
    alt:str=Field(default='',max_length=300)
    @model_validator(mode='after')
    def described(self):
        if self.shared and not self.alt:raise ValueError('Add a customer-visible image description before sharing.')
        return self


class PublicPhotoText(Strict):
    caption:str=Field(default='',max_length=200)
    alt:str=Field(min_length=1,max_length=300)


class ProjectImage(Strict):
    id:str=Field(pattern=ID)
    source_kind:Literal['job','website']
    source_id:str=Field(pattern=ID)
    caption:str=Field(default='',max_length=200)
    alt:str=Field(default='',max_length=300)
    phase:PHASE='general'
    pair_id:str=Field(default='',max_length=60,pattern=r'^(?:[a-z0-9]+(?:-[a-z0-9]+)*)?$')
    @model_validator(mode='after')
    def pairing(self):
        if self.pair_id and self.phase not in ('before','after'):raise ValueError('Choose Before or After for a paired image.')
        return self


class ProjectContent(Strict):
    content_version:Literal[1]=1
    title:str=Field(min_length=2,max_length=120)
    slug:str=Field(min_length=1,max_length=64)
    description:str=Field(default='',max_length=6000)
    category:str=Field(default='general',pattern=CATEGORY,max_length=60)
    meta_description:str=Field(default='',max_length=300)
    noindex:bool=Field(default=False,strict=True)
    in_index:bool=Field(default=True,strict=True)
    position:int=Field(default=0,strict=True,ge=0,le=9999)
    service_slugs:list[str]=Field(default_factory=list,max_length=30)
    images:list[ProjectImage]=Field(default_factory=list,max_length=60)
    @field_validator('slug')
    @classmethod
    def slug_shape(cls,value):return safe_slug(value)
    @field_validator('service_slugs')
    @classmethod
    def services(cls,value):
        if len(set(value))!=len(value):raise ValueError('Do not repeat a service association.')
        for slug in value:safe_slug(slug)
        return value
    @model_validator(mode='after')
    def unique_images(self):
        if len({i.id for i in self.images})!=len(self.images) or len({(i.source_kind,i.source_id) for i in self.images})!=len(self.images):raise ValueError('Each selected image must occur only once.')
        pairs={}
        for image in self.images:
            if image.pair_id:
                phases=pairs.setdefault(image.pair_id,set())
                if image.phase in phases:raise ValueError('A comparison has at most one Before and one After image.')
                phases.add(image.phase)
        return self


class ProjectCreate(Strict):
    source_job_id:str|None=Field(default=None,pattern=ID)
    content:ProjectContent


class ProjectEdit(Version):
    content:ProjectContent


class ProjectPublish(Version):
    permission_confirmed:bool=Field(strict=True)
    @field_validator('permission_confirmed')
    @classmethod
    def confirmed(cls,value):
        if not value:raise ValueError('Confirm permission to publish these images and text.')
        return value


class ProjectRestore(Version):
    revision_id:int=Field(strict=True,ge=1)
