import { serializeDraftDataForExport } from '@/components/applicant/applicationDrafts';
import {
  INITIAL_APPLICATION_FORM_STATE,
  INITIAL_REPORT_FORM_STATE,
} from '@/components/applicant/initiativeFormTypes';

/** One empty support row so every SupportStaff field appears in JSON. */
const EMPTY_SUPPORT_STAFF = {
  id: 1,
  name: '',
  dob: '',
  workplace: '',
  title: '',
  qualification: '',
  supportContent: '',
};

/** One empty trial unit row. */
const EMPTY_TRIAL_UNIT = {
  id: 1,
  name: '',
  address: '',
  field: '',
};

/** Participant row as persisted (no isEditing). */
const EMPTY_PARTICIPANT = {
  id: 1,
  fullName: '',
  workUnit: '',
  contributionPercent: 0,
};

function buildApplicationTemplate(): Record<string, unknown> {
  const a = INITIAL_APPLICATION_FORM_STATE;
  return {
    unitName: a.unitName,
    authors: a.authors.map((x) => ({ ...x })),
    initiativeName: a.initiativeName,
    investorName: a.investorName,
    applicationField: a.applicationField,
    firstApplyDate: a.firstApplyDate,
    initiativeClassification: a.initiativeClassification,
    textbookEvidenceFile: null,
    textbookEvidenceKind: a.textbookEvidenceKind,
    researchEvidenceKind: a.researchEvidenceKind,
    researchEvidenceFile: null,
    internationalJournalDeclaration: a.internationalJournalDeclaration,
    banCamKet: a.banCamKet,
    referenceMaterialHonesty: a.referenceMaterialHonesty,
    researchDomesticHonesty: a.researchDomesticHonesty,
    technicalEvidenceFile: null,
    contentSummary: a.contentSummary,
    confidentialInfo: a.confidentialInfo,
    conditions: a.conditions,
    authorEvaluation: a.authorEvaluation,
    trialEvaluation: a.trialEvaluation,
    supportStaff: a.supportStaff.length > 0 ? a.supportStaff.map((s) => ({ ...s })) : [{ ...EMPTY_SUPPORT_STAFF }],
    honestyConfirmed: a.honestyConfirmed,
    submissionDay: a.submissionDay,
    submissionMonth: a.submissionMonth,
    submissionYear: a.submissionYear,
  };
}

function buildReportTemplate(): Record<string, unknown> {
  const r = INITIAL_REPORT_FORM_STATE;
  return {
    introduction: r.introduction,
    initiativeName: r.initiativeName,
    representativeAuthor: r.representativeAuthor,
    representativePhone: r.representativePhone,
    representativeEmail: r.representativeEmail,
    applicationField: r.applicationField,
    currentStatus: r.currentStatus,
    purpose: r.purpose,
    solutionContent: r.solutionContent,
    implementationSteps: r.implementationSteps,
    firstAppliedUnit: r.firstAppliedUnit,
    achievedResult: r.achievedResult,
    conditions: r.conditions,
    trialUnits: r.trialUnits.length > 0 ? r.trialUnits.map((u) => ({ ...u })) : [{ ...EMPTY_TRIAL_UNIT }],
    novelty: r.novelty,
    effectiveness: { ...r.effectiveness },
    confidentialInfo: r.confidentialInfo,
    submissionDate: r.submissionDate,
    authorName: r.authorName,
    honestyConfirmed: r.honestyConfirmed,
  };
}

function buildContributionTemplate(): Record<string, unknown> {
  return {
    initiativeName: '',
    mainAuthor: '',
    position: '',
    representativePercent: 0,
    submissionDate: null,
    participants: [{ ...EMPTY_PARTICIPANT }],
    digitalSignatureConfirmed: false,
  };
}

function mergeRows<T extends Record<string, unknown>>(
  templateRow: T,
  rawList: unknown,
): T[] {
  if (!Array.isArray(rawList) || rawList.length === 0) return [{ ...templateRow }];
  return rawList.map((row, i) => {
    const o = row && typeof row === 'object' ? (row as Record<string, unknown>) : {};
    return { ...templateRow, ...o, id: typeof o.id === 'number' ? o.id : i + 1 } as T;
  });
}

function mergeAuthors(rawList: unknown): Record<string, unknown>[] {
  const templateRows = INITIAL_APPLICATION_FORM_STATE.authors.map((x) => ({ ...x }));
  const rowT = templateRows[0] as unknown as Record<string, unknown>;
  return mergeRows(rowT, rawList);
}

function mergeSupportStaff(rawList: unknown): Record<string, unknown>[] {
  return mergeRows({ ...EMPTY_SUPPORT_STAFF }, rawList);
}

function mergeTrialUnits(rawList: unknown): Record<string, unknown>[] {
  return mergeRows({ ...EMPTY_TRIAL_UNIT }, rawList);
}

function mergeParticipants(rawList: unknown): Record<string, unknown>[] {
  if (!Array.isArray(rawList) || rawList.length === 0) return [{ ...EMPTY_PARTICIPANT }];
  return rawList.map((row, i) => {
    const o = row && typeof row === 'object' ? (row as Record<string, unknown>) : {};
    const { isEditing, ...rest } = o;
    void isEditing;
    return {
      ...EMPTY_PARTICIPANT,
      ...rest,
      id: typeof o.id === 'number' ? o.id : i + 1,
    };
  });
}

function omitKeys(raw: Record<string, unknown>, keys: string[]): Record<string, unknown> {
  const out = { ...raw };
  for (const k of keys) delete out[k];
  return out;
}

function mergeScalarRecord(template: Record<string, unknown>, raw: Record<string, unknown>): Record<string, unknown> {
  const out: Record<string, unknown> = { ...template };
  for (const key of Object.keys(template)) {
    if (key in raw && raw[key] !== undefined) {
      const tv = template[key];
      const rv = raw[key];
      if (
        tv !== null &&
        typeof tv === 'object' &&
        !Array.isArray(tv) &&
        rv !== null &&
        typeof rv === 'object' &&
        !Array.isArray(rv)
      ) {
        out[key] = mergeScalarRecord(tv as Record<string, unknown>, rv as Record<string, unknown>);
      } else {
        out[key] = rv;
      }
    }
  }
  return out;
}

/**
 * Strings `''`, `undefined`, and empty arrays become `null` (booleans and numbers preserved,
 * except contribution `representativePercent === 0` → `null`).
 */
function emptyToNullDeep(value: unknown, options?: { contributionRoot?: boolean }): unknown {
  if (value === undefined || value === '') return null;
  if (value === null) return null;
  if (typeof value === 'boolean') return value;
  if (typeof value === 'number') return value;
  if (Array.isArray(value)) {
    if (value.length === 0) return null;
    return value.map((v) => emptyToNullDeep(v));
  }
  if (value && typeof value === 'object') {
    const o = value as Record<string, unknown>;
    const out: Record<string, unknown> = {};
    for (const [k, v] of Object.entries(o)) {
      out[k] = emptyToNullDeep(v);
    }
    if (options?.contributionRoot && out.representativePercent === 0) {
      out.representativePercent = null;
    }
    return out;
  }
  return value;
}

export function toFullApplicationJson(raw: Record<string, unknown>): Record<string, unknown> {
  const template = buildApplicationTemplate();
  const merged = mergeScalarRecord(template, omitKeys(raw, ['authors', 'supportStaff']));
  merged.authors = mergeAuthors(raw.authors);
  merged.supportStaff = mergeSupportStaff(raw.supportStaff);
  const serialized = serializeDraftDataForExport(merged) as Record<string, unknown>;
  return emptyToNullDeep(serialized) as Record<string, unknown>;
}

export function toFullReportJson(raw: Record<string, unknown>): Record<string, unknown> {
  const template = buildReportTemplate();
  const merged = mergeScalarRecord(template, omitKeys(raw, ['trialUnits']));
  merged.trialUnits = mergeTrialUnits(raw.trialUnits);
  const serialized = serializeDraftDataForExport(merged) as Record<string, unknown>;
  return emptyToNullDeep(serialized) as Record<string, unknown>;
}

export function toFullContributionJson(raw: Record<string, unknown>): Record<string, unknown> {
  const template = buildContributionTemplate();
  const merged = mergeScalarRecord(template, omitKeys(raw, ['participants']));
  merged.participants = mergeParticipants(raw.participants);
  const serialized = serializeDraftDataForExport(merged) as Record<string, unknown>;
  normalizeParticipantContributionZeros(serialized);
  return emptyToNullDeep(serialized, { contributionRoot: true }) as Record<string, unknown>;
}

function normalizeParticipantContributionZeros(root: Record<string, unknown>): void {
  const parts = root.participants;
  if (!Array.isArray(parts)) return;
  for (const row of parts) {
    if (row && typeof row === 'object' && (row as Record<string, unknown>).contributionPercent === 0) {
      (row as Record<string, unknown>).contributionPercent = null;
    }
  }
}
