"""Explicit, bounded customer identity reviews. No contact-based authorisation."""
from typing import Literal
from pydantic import Field, StrictBool, field_validator, model_validator
from .models import Strict
from .material_rules import Identity, VersionNumber
from .workflow_models import calendar_day

SCHEMA = '''
CREATE TABLE IF NOT EXISTS customer_records(customer_id TEXT PRIMARY KEY REFERENCES customers(id),
 state TEXT NOT NULL, canonical_id TEXT REFERENCES customers(id), notes TEXT NOT NULL, version INTEGER NOT NULL);
CREATE TABLE IF NOT EXISTS customer_record_history(id INTEGER PRIMARY KEY AUTOINCREMENT,
 customer_id TEXT NOT NULL REFERENCES customers(id), version INTEGER NOT NULL, action TEXT NOT NULL,
 actor TEXT NOT NULL, snapshot TEXT NOT NULL, reason TEXT NOT NULL, created_at TEXT NOT NULL,
 UNIQUE(customer_id,version));
CREATE TABLE IF NOT EXISTS customer_pair_decisions(id TEXT PRIMARY KEY, left_id TEXT NOT NULL REFERENCES customers(id),
 right_id TEXT NOT NULL REFERENCES customers(id), state TEXT NOT NULL, version INTEGER NOT NULL,
 reason TEXT NOT NULL, context_hash TEXT NOT NULL, context TEXT NOT NULL, updated_at TEXT NOT NULL);
CREATE TABLE IF NOT EXISTS customer_pair_history(id INTEGER PRIMARY KEY AUTOINCREMENT,
 pair_id TEXT NOT NULL REFERENCES customer_pair_decisions(id), version INTEGER NOT NULL,
 actor TEXT NOT NULL, snapshot TEXT NOT NULL, created_at TEXT NOT NULL, UNIQUE(pair_id,version));
CREATE TABLE IF NOT EXISTS customer_transfers(id TEXT PRIMARY KEY, kind TEXT NOT NULL,
 source_id TEXT NOT NULL REFERENCES customers(id), target_id TEXT NOT NULL REFERENCES customers(id),
 actor TEXT NOT NULL, reason TEXT NOT NULL, preview_hash TEXT NOT NULL, plan TEXT NOT NULL,
 result TEXT NOT NULL, created_at TEXT NOT NULL);
CREATE TABLE IF NOT EXISTS customer_transfer_jobs(job_id TEXT NOT NULL REFERENCES jobs(id),
 transfer_id TEXT NOT NULL REFERENCES customer_transfers(id), from_customer_id TEXT NOT NULL REFERENCES customers(id),
 to_customer_id TEXT NOT NULL REFERENCES customers(id), before_version INTEGER NOT NULL, after_version INTEGER NOT NULL,
 sequence INTEGER NOT NULL, previous_transfer_id TEXT REFERENCES customer_transfers(id),
 PRIMARY KEY(job_id,sequence), UNIQUE(job_id,transfer_id), UNIQUE(job_id,after_version));
CREATE TABLE IF NOT EXISTS customer_completion_runs(id TEXT PRIMARY KEY, actor TEXT NOT NULL, reason TEXT NOT NULL,
 preview_hash TEXT NOT NULL, plan TEXT NOT NULL, result TEXT NOT NULL, created_at TEXT NOT NULL);
'''
CORRECTION_SCHEMA = """CREATE TABLE customer_merge_corrections(
 id TEXT PRIMARY KEY, merge_id TEXT UNIQUE NOT NULL REFERENCES customer_transfers(id),
 alias_id TEXT NOT NULL REFERENCES customers(id), retained_id TEXT NOT NULL REFERENCES customers(id),
 actor TEXT NOT NULL, reason TEXT NOT NULL, preview_hash TEXT NOT NULL, plan TEXT NOT NULL,
 result TEXT NOT NULL, child_transfer_id TEXT UNIQUE REFERENCES customer_transfers(id),
 created_at TEXT NOT NULL, completed_at TEXT NOT NULL)"""
SCHEMA += CORRECTION_SCHEMA.replace('CREATE TABLE ','CREATE TABLE IF NOT EXISTS ')+';'
TABLES = ('customer_merge_corrections','customer_completion_runs','customer_transfer_jobs','customer_transfers','customer_pair_history',
          'customer_pair_decisions','customer_record_history','customer_records')

class Confirmation(Strict):
    confirmed: StrictBool
    reason: str = Field(min_length=5, max_length=1000)

    @field_validator('confirmed')
    @classmethod
    def deliberate(cls, value):
        if not value: raise ValueError('Review and explicitly confirm this action.')
        return value

    @field_validator('reason')
    @classmethod
    def explanation(cls, value):
        if len(value.strip()) < 5: raise ValueError('Add a meaningful reason.')
        return value.strip()

class RecordNotes(Strict):
    version: VersionNumber
    notes: str = Field(max_length=6000)

class RecordState(Confirmation):
    version: VersionNumber
    state: Literal['active','archived']

class FieldChoices(Strict):
    name: Literal['source','target'] = 'target'
    email: Literal['source','target'] = 'target'
    phone: Literal['source','target'] = 'target'
    address: Literal['source','target'] = 'target'

class TransferSelection(Strict):
    kind: Literal['merge','jobs']
    source_id: Identity
    target_id: Identity
    source_version: VersionNumber
    target_version: VersionNumber
    job_ids: list[Identity] = Field(default_factory=list, max_length=25)
    fields: FieldChoices = Field(default_factory=FieldChoices)

    @model_validator(mode='after')
    def exact_selection(self):
        if self.source_id == self.target_id: raise ValueError('Select two different records.')
        if len(set(self.job_ids)) != len(self.job_ids): raise ValueError('Select each job once.')
        if (self.kind == 'merge' and self.job_ids) or (self.kind == 'jobs' and not self.job_ids):
            raise ValueError('Merge includes all source jobs; reassignment requires selected exact jobs.')
        if self.kind == 'jobs' and any(v != 'target' for v in self.fields.model_dump().values()):
            raise ValueError('A job reassignment does not change contact details.')
        return self

class TransferConfirm(TransferSelection, Confirmation):
    preview_hash: str = Field(pattern=r'^[a-f0-9]{64}$')

class PairDecision(Confirmation):
    left_id: Identity
    right_id: Identity
    version: int = Field(strict=True, ge=0)
    context_hash: str = Field(pattern=r'^[a-f0-9]{64}$')
    state: Literal['keep_separate','review']

class CompletionRow(Strict):
    customer_id: Identity
    customer_version: VersionNumber
    job_id: Identity
    job_version: VersionNumber
    invoice_id: Identity
    completed_date: str
    evidence_note: str = Field(min_length=5, max_length=500)

    @field_validator('completed_date')
    @classmethod
    def date_shape(cls, value):
        if not value: raise ValueError('Enter an evidenced work completion date.')
        return calendar_day(value)

    @field_validator('evidence_note')
    @classmethod
    def provenance(cls, value):
        if len(value.strip()) < 5: raise ValueError('Explain the source of this date.')
        return value.strip()

class CompletionPreview(Strict):
    rows: list[CompletionRow] = Field(min_length=1, max_length=25)

class CompletionConfirm(CompletionPreview, Confirmation):
    preview_hash: str = Field(pattern=r'^[a-f0-9]{64}$')


class MergeCorrectionFields(Strict):
    name: Literal['keep_current','restore_before_merge'] = 'keep_current'
    email: Literal['keep_current','restore_before_merge'] = 'keep_current'
    phone: Literal['keep_current','restore_before_merge'] = 'keep_current'
    address: Literal['keep_current','restore_before_merge'] = 'keep_current'

class MergeCorrectionSelection(Strict):
    merge_id: Identity
    alias_id: Identity
    retained_id: Identity
    alias_version: VersionNumber
    retained_version: VersionNumber
    job_ids: list[Identity] = Field(default_factory=list,max_length=25)
    fields: MergeCorrectionFields = Field(default_factory=MergeCorrectionFields)

    @model_validator(mode='after')
    def exact_selection(self):
        if self.alias_id==self.retained_id:raise ValueError('Review two different exact records.')
        if len(set(self.job_ids))!=len(self.job_ids):raise ValueError('Select each original job once.')
        return self

class MergeCorrectionConfirm(MergeCorrectionSelection,Confirmation):
    preview_hash: str = Field(pattern=r'^[a-f0-9]{64}$')
