import type { ComponentType } from 'react'
import { BookOpen, Building2, CalendarClock, FileText, Image as ImageIcon, Pencil, UserRound } from 'lucide-react'
import type { CaseDocument, Connection, DocumentCaptureKind, DocumentExhibit, Evidence, Exhibit, ExhibitRelation, ExhibitType, PartyExhibit, SourceFileType, TemporalFact } from './types'

export type WidgetCommand =
  | { type: 'open-document'; documentId: string }
  | { type: 'edit-folder'; folderId: string }
  | { type: 'edit-event'; eventId: string }
  | { type: 'edit-party'; partyId: string }
  | { type: 'edit-document'; documentId: string }
  | { type: 'update-memory-cue'; documentId: string; cue: string }

export type ExhibitWidgetContext = {
  exhibits: Exhibit[]
  relations: ExhibitRelation[]
  dispatch: (command: WidgetCommand) => void
}

export type ExhibitWidgetProps = { exhibit: Exhibit; context: ExhibitWidgetContext }
export type WidgetCapabilities = { movable: boolean; resizable: boolean; connectable: boolean; discardable: boolean; dockable: boolean }
export type ConnectionPort = { id: string; x: number; y: number }

export type ExhibitWidgetDefinition = {
  modelKind: 'exhibit'
  visualType: ExhibitType
  shell: 'card' | 'document'
  defaultSize: { width: number; height: number }
  capabilities: WidgetCapabilities
  heading: (exhibit: Exhibit, context: ExhibitWidgetContext) => string
  connectionPorts: (exhibit: Exhibit) => ConnectionPort[]
  temporalFacts: (exhibit: Exhibit) => TemporalFact[]
  searchText: (exhibit: Exhibit) => string
  Component: ComponentType<ExhibitWidgetProps>
}

const relationsFrom = (context: ExhibitWidgetContext, exhibitId: string, type: ExhibitRelation['type']) => context.relations
  .filter(relation => relation.type === type && relation.fromExhibitId === exhibitId)
  .sort((a, b) => a.sortOrder - b.sortOrder)

function FolderWidget({ exhibit, context }: ExhibitWidgetProps) {
  if (exhibit.type !== 'folder') return null
  return <div className="card-content"><h3>{exhibit.title}</h3><p>{exhibit.content}</p>
    <div className="folder-actions"><button onClick={() => context.dispatch({ type:'edit-folder',folderId:exhibit.id })}><Pencil size={15}/> EDIT</button></div>
  </div>
}

function NoteWidget({ exhibit, context }: ExhibitWidgetProps) {
  if (exhibit.type !== 'note') return null
  const source = relationsFrom(context, exhibit.id, 'source')[0]
  return <div className="card-content"><h3>{exhibit.title}</h3><p>{exhibit.content}</p>
    {source && <button onClick={() => context.dispatch({ type:'open-document',documentId:source.toExhibitId })}><BookOpen size={13}/> VIEW SOURCE</button>}
  </div>
}

function EventWidget({ exhibit, context }: ExhibitWidgetProps) {
  if (exhibit.type !== 'event') return null
  const supportCount = relationsFrom(context,exhibit.id,'supports').length
  return <div className="card-content"><h3>{exhibit.title}</h3><p>{exhibit.content}</p>
    <time>{exhibit.eventDate ? new Date(exhibit.eventDate).toLocaleString() : 'UNDATED'}</time>
    <div className="event-actions"><span>{supportCount} SUPPORTING EXHIBIT{supportCount === 1 ? '' : 'S'}</span><button onClick={() => context.dispatch({ type:'edit-event',eventId:exhibit.id })}><CalendarClock size={12}/> EDIT EVENT</button></div>
  </div>
}

function PartyWidget({ exhibit, context }: ExhibitWidgetProps) {
  if (exhibit.type !== 'party') return null
  const person = exhibit.partyKind === 'person'
  const evidenceCount = relationsFrom(context,exhibit.id,'concerns').length
  return <div className="card-content"><div className="party-identity">{person ? <UserRound size={28}/> : <Building2 size={28}/>}<div><h3>{exhibit.title}</h3><small>{person ? 'PERSON' : (exhibit.organizationKind || 'ORGANIZATION').replaceAll('_',' ').toUpperCase()}</small></div></div>
    <p>{exhibit.content || 'No dossier summary yet.'}</p>
    {exhibit.aliases.length > 0 && <div className="party-aliases">AKA · {exhibit.aliases.join(' · ')}</div>}
    <div className="event-actions"><span>{evidenceCount} ASSOCIATED EXHIBITS</span><button onClick={() => context.dispatch({ type:'edit-party',partyId:exhibit.id })}><Pencil size={12}/> EDIT DOSSIER</button></div>
  </div>
}

function ClaimWidget({ exhibit }: ExhibitWidgetProps) {
  if (exhibit.type !== 'claim') return null
  return <div className="card-content claim-content"><blockquote>{exhibit.statement}</blockquote></div>
}

function DocumentWidget({ exhibit }: ExhibitWidgetProps) {
  if (exhibit.type !== 'document') return null
  return <strong>{exhibit.title}</strong>
}

const standardPorts = (exhibit: Exhibit) => [{ id:'centre',x:exhibit.x + exhibit.width / 2,y:exhibit.y + exhibit.height / 2 }]
const notePorts = (exhibit: Exhibit) => [{ id:'knot',x:exhibit.x + exhibit.width / 2,y:exhibit.y + 12 }]
const standardCapabilities: WidgetCapabilities = { movable:true,resizable:false,connectable:true,discardable:true,dockable:false }
const searchable = (exhibit: Exhibit) => exhibit.type === 'document'
  ? [exhibit.title,...exhibit.body,...Object.values(exhibit.metadata)].join('\n').toLocaleLowerCase()
  : exhibit.type === 'claim' ? [exhibit.title,exhibit.statement].join('\n').toLocaleLowerCase()
  : [exhibit.title,exhibit.content].join('\n').toLocaleLowerCase()

export const exhibitWidgetRegistry: Record<ExhibitType, ExhibitWidgetDefinition> = {
  folder: { modelKind:'exhibit',visualType:'folder',shell:'card',defaultSize:{width:260,height:166},capabilities:standardCapabilities,
    heading:(exhibit,context) => `EVIDENCE FOLDER / ${relationsFrom(context,exhibit.id,'contains').length}`,connectionPorts:standardPorts,temporalFacts:() => [],searchText:searchable,Component:FolderWidget },
  document: { modelKind:'exhibit',visualType:'document',shell:'document',defaultSize:{width:174,height:145},capabilities:standardCapabilities,
    heading:exhibit => exhibit.type === 'document' ? documentWidget(exhibit.fileType).label.toUpperCase() : 'DOCUMENT',connectionPorts:standardPorts,
    temporalFacts:exhibit => exhibit.type === 'document' ? [
      ...(exhibit.publishedAt ? [{ id:`${exhibit.id}:published`,exhibitId:exhibit.id,kind:'published' as const,start:exhibit.publishedAt,label:exhibit.title }] : []),
      ...(exhibit.capturedAt ? [{ id:`${exhibit.id}:captured`,exhibitId:exhibit.id,kind:'captured' as const,start:exhibit.capturedAt,label:`${exhibit.title} captured` }] : []),
      ...exhibit.regions.flatMap(region => region.date ? [{ id:`${exhibit.id}:region:${region.id}`,exhibitId:exhibit.id,kind:'region_date' as const,start:region.date,label:region.label }] : []),
    ] : [],searchText:searchable,Component:DocumentWidget },
  note: { modelKind:'exhibit',visualType:'note',shell:'card',defaultSize:{width:108,height:154},capabilities:standardCapabilities,
    heading:exhibit => exhibit.type === 'note' && exhibit.presentation === 'lined_sheet' ? 'FIELD NOTE / TORN PAGE' : 'INVESTIGATOR / NOTE',connectionPorts:notePorts,temporalFacts:() => [],searchText:searchable,Component:NoteWidget },
  event: { modelKind:'exhibit',visualType:'event',shell:'card',defaultSize:{width:270,height:174},capabilities:standardCapabilities,
    heading:() => 'EVENT / THIS HAPPENED',connectionPorts:standardPorts,temporalFacts:exhibit => exhibit.type === 'event' && exhibit.eventDate
      ? [{ id:`${exhibit.id}:occurred`,exhibitId:exhibit.id,kind:'occurred',start:exhibit.eventDate,label:exhibit.content }] : [],searchText:searchable,Component:EventWidget },
  party: { modelKind:'exhibit',visualType:'party',shell:'card',defaultSize:{width:280,height:190},capabilities:standardCapabilities,
    heading:exhibit => exhibit.type === 'party' && exhibit.partyKind === 'person' ? 'PARTY / PERSON DOSSIER' : 'PARTY / ORGANIZATION DOSSIER',connectionPorts:standardPorts,temporalFacts:() => [],searchText:searchable,Component:PartyWidget },
  claim: { modelKind:'exhibit',visualType:'claim',shell:'card',defaultSize:{width:310,height:180},capabilities:{...standardCapabilities,discardable:false},
    heading:() => 'CASE CLAIM / UNPROVEN',connectionPorts:standardPorts,temporalFacts:() => [],searchText:searchable,Component:ClaimWidget },
}

export function exhibitWidget(type: ExhibitType) { return exhibitWidgetRegistry[type] }

type DocumentWidgetProps = { document: CaseDocument; source: string; onMemoryCue?: (cue: string) => void }
export type DocumentWidgetDefinition = { label:string; Preview:ComponentType<DocumentWidgetProps>; Asset:ComponentType<DocumentWidgetProps> }

function ImagePreview({ document,source }: DocumentWidgetProps) { return document.assetId ? <img draggable={false} src={source} alt=""/> : <GenericPreview document={document} source={source}/> }
function GenericPreview({ document }: DocumentWidgetProps) { return <div><ImageIcon size={35}/><small>{document.fileType.toUpperCase()}</small></div> }
function TextPreview({ document,onMemoryCue }: DocumentWidgetProps) {
  const excerpt = document.body.filter(Boolean).slice(0,2).join(' ')
  return <div className="text-document-preview"><p className="text-source-excerpt">{excerpt || document.title}</p><textarea aria-label={`Memory cue for ${document.title}`} maxLength={48} placeholder="WRITE A MEMORY CUE…" value={document.metadata.memory_cue || ''} onPointerDown={event => event.stopPropagation()} onClick={event => event.stopPropagation()} onDoubleClick={event => event.stopPropagation()} onChange={event => onMemoryCue?.(event.target.value)}/></div>
}
function ImageAsset({ document,source }: DocumentWidgetProps) { return <img className="document-image" src={source} alt={document.fileName || document.title}/> }
function FrameAsset({ document,source }: DocumentWidgetProps) { return <iframe className="document-frame" src={source} title={document.fileName || document.title} sandbox="allow-same-origin"/> }
function GenericAsset({ document,source }: DocumentWidgetProps) { return <div className="unsupported-file"><FileText size={42}/><b>{document.fileName || document.title}</b><span>{document.mimeType || 'Unknown file type'} · {document.fileSize ? `${Math.ceil(document.fileSize / 1024)} KB` : ''}</span><a href={source} download={document.fileName}>DOWNLOAD ORIGINAL</a></div> }

const genericDocument = (label:string):DocumentWidgetDefinition => ({label,Preview:GenericPreview,Asset:GenericAsset})
export const documentWidgetRegistry:Record<SourceFileType,DocumentWidgetDefinition> = {
  image:{label:'Image',Preview:ImagePreview,Asset:ImageAsset},pdf:{label:'PDF',Preview:GenericPreview,Asset:FrameAsset},text:{label:'Text document',Preview:TextPreview,Asset:FrameAsset},
  web_capture:genericDocument('Web capture'),email:genericDocument('Email'),article:genericDocument('Article'),filing:genericDocument('Company filing'),price_list:genericDocument('Price list'),file:genericDocument('Generic file'),
}
export function documentWidget(type:SourceFileType) { return documentWidgetRegistry[type] || documentWidgetRegistry.file }

export type DocumentCaptureDefinition = {
  label:string
  description:string
  defaultSize:{ width:number;height:number }
}
export const documentCaptureRegistry:Record<DocumentCaptureKind,DocumentCaptureDefinition> = {
  unclassified:{ label:'Not sure',description:'Keep the standard evidence card for now.',defaultSize:{width:174,height:145} },
  photo:{ label:'Mugshot',description:'A portrait or identifying photograph.',defaultSize:{width:188,height:250} },
  scene:{ label:'Image',description:'A place, situation, object, or event is shown.',defaultSize:{width:244,height:200} },
  clipping:{ label:'Clip',description:'An extract mounted on a physical evidence card.',defaultSize:{width:230,height:290} },
  full_page:{ label:'Document',description:'A complete page or formal document view.',defaultSize:{width:205,height:294} },
}
export function documentCapture(kind:DocumentCaptureKind) { return documentCaptureRegistry[kind] || documentCaptureRegistry.unclassified }

export type MugshotIdentification = { party:PartyExhibit;connection:Connection }

/** A Mugshot caption is a projection of its latest Party connection, never copied document metadata. */
export function mugshotIdentification(document:DocumentExhibit,exhibits:Exhibit[],connections:Connection[]):MugshotIdentification | null {
  if (document.captureKind !== 'photo') return null
  const byId=new Map(exhibits.map(exhibit => [exhibit.id,exhibit]))
  for (let index=connections.length - 1;index >= 0;index--) {
    const connection=connections[index]
    const otherId=connection.fromExhibitId === document.id ? connection.toExhibitId : connection.toExhibitId === document.id ? connection.fromExhibitId : null
    const other=otherId ? byId.get(otherId) : null
    if (other?.type === 'party') return { party:other,connection }
  }
  return null
}

/** Context-sensitive starter copy. A Party is never treated as proof by default. */
export function defaultConnectionLabel(first:Exhibit,second:Exhibit) {
  const types = new Set([first.type,second.type])
  if (types.has('party') && types.has('claim')) return 'Subject of claim'
  const document = first.type === 'document' ? first : second.type === 'document' ? second : null
  if (document && types.has('party')) {
    if (document.captureKind === 'photo') return 'Identified as…'
    if (document.captureKind === 'scene') return 'Shows…'
    return 'Concerns…'
  }
  return 'Proof that…'
}

export function documentExhibits(exhibits:Exhibit[]):DocumentExhibit[] { return exhibits.filter((exhibit):exhibit is DocumentExhibit => exhibit.type === 'document') }
export function evidenceExhibits(exhibits:Exhibit[]):Evidence[] { return exhibits.filter((exhibit):exhibit is Evidence => exhibit.type !== 'document') }
