import { db } from '../db/client.js';

const AUTOMATION_CONTEXTS_TABLE = 'automation_contexts';

export type AutomationContextRecord = {
  id: string;
  title: string;
  content: string;
  enabled: boolean;
  defaultSelected: boolean;
  updatedAt: string;
};

export const DEFAULT_AUTOMATION_CONTEXTS: AutomationContextRecord[] = [
  {
    id: 'general-inquiry-default',
    title: '기본 확인',
    content: '소스 수정 없이 조회, 상태 확인, 응답 정리를 우선합니다.',
    enabled: true,
    defaultSelected: true,
    updatedAt: '2026-04-29T00:00:00.000Z',
  },
  {
    id: 'none-default',
    title: '기본 처리',
    content:
      '## 기본 처리\n- 현재 저장소 규칙과 요청 본문을 우선 따릅니다.\n- 소스 수정이 필요하면 관련 파일을 수정하고, 필요 없으면 확인 결과만 정리합니다.\n- 작업 결과에는 변경 파일, diff, preview 같은 증적을 남길 수 있습니다.',
    enabled: true,
    defaultSelected: true,
    updatedAt: '2026-04-29T00:00:00.000Z',
  },
  {
    id: 'plan-default',
    title: '문서형 처리',
    content: 'Plan 등록/문서형 요청으로 처리하고, 구현보다 등록/정리 결과를 우선 남깁니다.',
    enabled: true,
    defaultSelected: false,
    updatedAt: '2026-04-29T00:00:00.000Z',
  },
  {
    id: 'command-execution-default',
    title: '명령 실행',
    content: '저장소 수정 없이 명령 실행, API 확인, 상태 조회 중심으로 처리합니다.',
    enabled: true,
    defaultSelected: false,
    updatedAt: '2026-04-29T00:00:00.000Z',
  },
  {
    id: 'non-source-work-default',
    title: '비소스 작업',
    content: '문서, 운영, 데이터 확인 등 비소스 작업으로 처리하고 코드 수정은 요청 시에만 제한적으로 수행합니다.',
    enabled: true,
    defaultSelected: false,
    updatedAt: '2026-04-29T00:00:00.000Z',
  },
  {
    id: 'auto-worker-default',
    title: '자동화 기본 규칙',
    content:
      '## context 사용 규칙\n- 자동화 실행기는 선택된 Context만 우선 참조합니다.\n- Codex Live 문맥이나 일반 채팅 문맥은 자동화 기본 context로 섞지 않습니다.\n\n## 처리 원칙\n- 세부 절차는 현재 운영 설정을 따릅니다.',
    enabled: true,
    defaultSelected: true,
    updatedAt: '2026-04-29T00:00:00.000Z',
  },
];

function normalizeText(value: unknown) {
  return typeof value === 'string' ? value.trim() : '';
}

function normalizeEnabled(value: unknown) {
  if (typeof value === 'boolean') {
    return value;
  }

  if (typeof value === 'number') {
    return value !== 0;
  }

  if (typeof value === 'string') {
    const normalizedValue = value.trim().toLowerCase();

    if (['false', '0', 'n', 'no', 'off'].includes(normalizedValue)) {
      return false;
    }

    if (['true', '1', 'y', 'yes', 'on'].includes(normalizedValue)) {
      return true;
    }
  }

  return value !== false;
}

function buildContextTitleKey(value: string) {
  return value.replace(/\s+/g, ' ').toLocaleLowerCase('ko-KR');
}

function compareContextUpdatedAt(left: AutomationContextRecord, right: AutomationContextRecord) {
  const leftTime = Date.parse(left.updatedAt);
  const rightTime = Date.parse(right.updatedAt);

  if (Number.isFinite(leftTime) && Number.isFinite(rightTime) && leftTime !== rightTime) {
    return leftTime - rightTime;
  }

  return 0;
}

function normalizeAutomationContext(record: Partial<AutomationContextRecord>): AutomationContextRecord | null {
  const title = normalizeText(record.title);
  const content = normalizeText(record.content);

  if (!title && !content) {
    return null;
  }

  const rawId = normalizeText(record.id);
  const normalizedId =
    rawId || `automation-context-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;

  return {
    id: normalizedId,
    title: title || 'Context',
    content,
    enabled: normalizeEnabled(record.enabled),
    defaultSelected: normalizeEnabled(record.defaultSelected),
    updatedAt: normalizeText(record.updatedAt) || new Date().toISOString(),
  };
}

export function sanitizeAutomationContexts(items: Partial<AutomationContextRecord>[] | null | undefined) {
  const byId = new Map<string, AutomationContextRecord>();
  const bySemanticKey = new Map<string, AutomationContextRecord>();

  (items ?? [])
    .map((item) => normalizeAutomationContext(item))
    .filter((item): item is AutomationContextRecord => Boolean(item))
    .forEach((item) => {
      const currentById = byId.get(item.id);
      if (!currentById || compareContextUpdatedAt(currentById, item) <= 0) {
        byId.set(item.id, item);
      }
    });

  for (const item of byId.values()) {
    const semanticKey = buildContextTitleKey(item.title);
    const current = bySemanticKey.get(semanticKey);

    if (!current || compareContextUpdatedAt(current, item) <= 0) {
      bySemanticKey.set(semanticKey, item);
    }
  }

  const values = Array.from(bySemanticKey.values()).sort((left, right) => left.title.localeCompare(right.title, 'ko-KR'));
  return values.length > 0 ? values : DEFAULT_AUTOMATION_CONTEXTS;
}

async function ensureAutomationContextsTable() {
  const hasTable = await db.schema.hasTable(AUTOMATION_CONTEXTS_TABLE);

  if (!hasTable) {
    await db.schema.createTable(AUTOMATION_CONTEXTS_TABLE, (table) => {
      table.string('id').primary();
      table.string('title').notNullable();
      table.text('content').notNullable().defaultTo('');
      table.boolean('enabled').notNullable().defaultTo(true);
      table.boolean('default_selected').notNullable().defaultTo(false);
      table.timestamp('updated_at', { useTz: true }).notNullable().defaultTo(db.fn.now());
    });
    return;
  }

  const requiredColumns: Array<[string, (table: any) => void]> = [
    ['title', (table) => table.string('title').notNullable().defaultTo('')],
    ['content', (table) => table.text('content').notNullable().defaultTo('')],
    ['enabled', (table) => table.boolean('enabled').notNullable().defaultTo(true)],
    ['default_selected', (table) => table.boolean('default_selected').notNullable().defaultTo(false)],
    ['updated_at', (table) => table.timestamp('updated_at', { useTz: true }).notNullable().defaultTo(db.fn.now())],
  ];

  for (const [columnName, createColumn] of requiredColumns) {
    const hasColumn = await db.schema.hasColumn(AUTOMATION_CONTEXTS_TABLE, columnName);

    if (!hasColumn) {
      await db.schema.alterTable(AUTOMATION_CONTEXTS_TABLE, (table) => {
        createColumn(table);
      });
    }
  }
}

function parseContextsFromLegacyValue(value: unknown) {
  if (typeof value !== 'string') {
    return [];
  }

  try {
    const parsed = JSON.parse(value);
    return Array.isArray(parsed) ? parsed : [];
  } catch {
    return [];
  }
}

function toAutomationContextRecord(row: Record<string, unknown>) {
  return normalizeAutomationContext({
    id: typeof row.id === 'string' ? row.id : undefined,
    title: typeof row.title === 'string' ? row.title : undefined,
    content: typeof row.content === 'string' ? row.content : undefined,
    enabled: normalizeEnabled(row.enabled),
    defaultSelected: normalizeEnabled(row.default_selected),
    updatedAt: typeof row.updated_at === 'string' ? row.updated_at : undefined,
  });
}

async function replaceAutomationContextsInTable(items: AutomationContextRecord[]) {
  await ensureAutomationContextsTable();

  const nextItems = sanitizeAutomationContexts(items);

  await db.transaction(async (trx) => {
    await trx(AUTOMATION_CONTEXTS_TABLE).del();
    await trx(AUTOMATION_CONTEXTS_TABLE).insert(
      nextItems.map((item) => ({
        id: item.id,
        title: item.title,
        content: item.content,
        enabled: item.enabled,
        default_selected: item.defaultSelected,
        updated_at: item.updatedAt,
      })),
    );
  });

  return nextItems;
}

async function seedAutomationContextsFromLegacySources() {
  const seededItems: Partial<AutomationContextRecord>[] = [...DEFAULT_AUTOMATION_CONTEXTS];

  const hasAutomationTypesTable = await db.schema.hasTable('automation_types');
  if (hasAutomationTypesTable) {
    const rows = await db('automation_types').select('contexts_json');
    for (const row of rows) {
      seededItems.push(...parseContextsFromLegacyValue((row as Record<string, unknown>).contexts_json));
    }
  }

  return replaceAutomationContextsInTable(sanitizeAutomationContexts(seededItems));
}

function isSameAutomationContextList(left: AutomationContextRecord[], right: AutomationContextRecord[]) {
  if (left.length !== right.length) {
    return false;
  }

  return left.every((item, index) => {
    const target = right[index];
    return (
      target &&
      item.id === target.id &&
      item.title === target.title &&
      item.content === target.content &&
      item.enabled === target.enabled &&
      item.defaultSelected === target.defaultSelected &&
      item.updatedAt === target.updatedAt
    );
  });
}

function mergeDefaultAutomationContexts(items: AutomationContextRecord[]) {
  const byId = new Map(items.map((item) => [item.id, item] as const));

  for (const defaultItem of DEFAULT_AUTOMATION_CONTEXTS) {
    const existingItem = byId.get(defaultItem.id);

    if (!existingItem) {
      byId.set(defaultItem.id, defaultItem);
      continue;
    }

    byId.set(defaultItem.id, {
      ...existingItem,
      title: defaultItem.title,
      content: existingItem.content || defaultItem.content,
    });
  }

  return sanitizeAutomationContexts(Array.from(byId.values()));
}

async function readAutomationContextsFromTable() {
  await ensureAutomationContextsTable();

  const rows = await db(AUTOMATION_CONTEXTS_TABLE)
    .select('id', 'title', 'content', 'enabled', 'default_selected', 'updated_at')
    .orderBy('title', 'asc');

  const savedItems = rows
    .map((row) => toAutomationContextRecord(row as Record<string, unknown>))
    .filter((item): item is AutomationContextRecord => Boolean(item));

  return sanitizeAutomationContexts(savedItems);
}

export async function getAutomationContextsConfig() {
  const savedContexts = await readAutomationContextsFromTable();

  if (savedContexts.length === 0 || savedContexts === DEFAULT_AUTOMATION_CONTEXTS) {
    return seedAutomationContextsFromLegacySources();
  }

  const mergedContexts = mergeDefaultAutomationContexts(savedContexts);
  if (!isSameAutomationContextList(savedContexts, mergedContexts)) {
    await replaceAutomationContextsInTable(mergedContexts);
  }

  return mergedContexts;
}

export async function upsertAutomationContextsConfig(items: unknown[]) {
  const nextContexts = mergeDefaultAutomationContexts(
    sanitizeAutomationContexts(Array.isArray(items) ? (items as Partial<AutomationContextRecord>[]) : []),
  );

  return replaceAutomationContextsInTable(nextContexts);
}

export function normalizeAutomationContextSelection(value: unknown) {
  const rawValues = Array.isArray(value)
    ? value
    : typeof value === 'string'
      ? value
          .split(',')
          .map((item) => item.trim())
          .filter(Boolean)
      : [];

  return [...new Set(rawValues.map((item) => normalizeText(String(item))).filter(Boolean))];
}

export function resolveAutomationContexts(
  contexts: AutomationContextRecord[] | null | undefined,
  selectedContextIds?: unknown,
) {
  const normalizedContexts = sanitizeAutomationContexts(contexts);
  const requestedIds = normalizeAutomationContextSelection(selectedContextIds);

  if (requestedIds.length === 0 && Array.isArray(selectedContextIds)) {
    return [];
  }

  if (requestedIds.length === 0) {
    return normalizedContexts.filter((item) => item.enabled && item.defaultSelected);
  }

  const requestedIdSet = new Set(requestedIds);
  return normalizedContexts.filter((item) => requestedIdSet.has(item.id));
}
