import { sql } from 'drizzle-orm';
import type { ContentSchema } from '@platform-modules/content';
import { listTerms, termsForEntry } from '@platform-modules/content/taxonomy';
import type { Querier } from '@platform-modules/db';
import type { CommentsSchema, Comment } from '@platform-modules/comments';
import type { I18nContentSchema } from '@platform-modules/i18n-content';
import type { FieldsImportRemappers, MediaFieldRef, TermFieldRef } from '@platform-modules/fields';
import type { FullDatabase } from './db.js';
import { getMedia, listMedia, type MediaRow, type MediaSchema } from './media.js';
import { loadPublicComments } from './comments-render.js';
import { resolveContentTranslations } from './content-i18n.js';
import { loadPublicContentSearch, type PublicContentSearchPage } from './content-search.js';

export interface ContentModelTermRef { readonly id:string; readonly taxonomy:string; readonly slug:string; readonly name:string; readonly parentId:string|null; readonly depth:number }

export interface ContentModelHostAdapters {
  readonly media: {
    get(key:string):Promise<MediaRow|null>;
    list(input?:{limit?:number;cursor?:string}):Promise<{rows:MediaRow[];cursor?:string}>;
  };
  readonly taxonomy: {
    list(taxonomy:string):Promise<ContentModelTermRef[]>;
    forEntry(entryId:string):Promise<ContentModelTermRef[]>;
  };
  readonly comments: {
    forEntry(entryId:string):Promise<{total:number;comments:Comment[]}>;
  };
  readonly translations: {
    resolve(input:{entityIds:readonly string[];locale:string;fields?:readonly string[];fallbackToDefault?:boolean}):Promise<Map<string,Record<string,string>>>;
  };
  readonly search: {
    public(query:string,cursor?:string|null):Promise<PublicContentSearchPage>;
  };
  readonly fieldsImportRemappers: FieldsImportRemappers;
}

function rowsOf<Row>(value:unknown):readonly Row[]{if(Array.isArray(value))return value as Row[];const rows=(value as {rows?:unknown})?.rows;return Array.isArray(rows)?rows as Row[]:[]}

export function createContentModelHostAdapters(db:FullDatabase):ContentModelHostAdapters {
  const contentDb:Querier<ContentSchema>=db,mediaDb:Querier<MediaSchema>=db,commentsDb:Querier<CommentsSchema>=db,translationDb:Querier<I18nContentSchema>=db;
  const contentExists=async(id:string):Promise<boolean>=>rowsOf(await db.execute(sql`SELECT id FROM content_entries WHERE id=${id} LIMIT 1`)).length===1;
  const userExists=async(id:string):Promise<boolean>=>rowsOf(await db.execute(sql`SELECT id FROM auth_users WHERE id=${id} LIMIT 1`)).length===1;
  const reusableBlockExists=async(id:string):Promise<boolean>=>rowsOf(await db.execute(sql`SELECT id FROM field_reusable_blocks WHERE id=${id} AND deleted_at IS NULL LIMIT 1`)).length===1;
  const remappers:FieldsImportRemappers=Object.freeze({
    entity:async(ref:Parameters<FieldsImportRemappers['entity']>[0])=>ref.entityType==='content'?(await contentExists(ref.entityId)?ref:null):ref,
    user:async(id:string)=>await userExists(id)?id:null,
    media:async(ref:MediaFieldRef)=>{const row=await getMedia(mediaDb,ref.id);if(!row)return null;if(ref.mimeType!==undefined&&row.contentType!==ref.mimeType)return null;return ref},
    term:async(ref:TermFieldRef)=>{const terms=await listTerms(contentDb,ref.taxonomy);return terms.some((term:ContentModelTermRef)=>term.id===ref.id)?ref:null},
    reusableBlock:async(id:string)=>await reusableBlockExists(id)?id:null,
  });
  return Object.freeze({
    media:Object.freeze({get:(key:string)=>getMedia(mediaDb,key),list:(input?:{limit?:number;cursor?:string})=>listMedia(mediaDb,input)}),
    taxonomy:Object.freeze({list:(taxonomy:string)=>listTerms(contentDb,taxonomy),forEntry:(entryId:string)=>termsForEntry(contentDb,entryId)}),
    comments:Object.freeze({forEntry:(entryId:string)=>loadPublicComments(commentsDb,{type:'content_entry',id:entryId})}),
    translations:Object.freeze({resolve:(input:{entityIds:readonly string[];locale:string;fields?:readonly string[];fallbackToDefault?:boolean})=>resolveContentTranslations(translationDb,{entityType:'content',...input})}),
    search:Object.freeze({public:(query:string,cursor?:string|null)=>loadPublicContentSearch(contentDb,query,cursor)}),
    fieldsImportRemappers:remappers,
  });
}
