import type { Template } from '@pdfme/common';
import type {
  DocumentTemplateMappingWithColumns,
  ResolvedDocumentTemplate,
} from '@/features/foundation/document-template/types';
import type { QuotationDocumentData } from '../types';
import { buildPdfTopicPages } from './pdf-topic-engine';
import { resolveTemplatePages } from './page-resolver';
import { buildProductItemSectionModel } from './product-item-engine';
import { createRenderContext, resolveRenderPolicies } from './render-context';
import { resolveQuotationStaticTemplateInputs } from './static-input-resolver';
import type {
  BuiltSection,
  ProductItemSection,
  QuotationPdfRuntimeResult,
  RenderContext,
  ResolvedTemplate,
  SectionBuilder,
} from './runtime-contracts';
import { composeSections } from './section-composer';
import { adaptTemplateForSectionRuntime } from './template-compatibility-adapter';
import { assembleTemplate } from './template-assembler';

function cloneTemplate<T>(value: T): T {
  return JSON.parse(JSON.stringify(value)) as T;
}

function buildMissingPageSection<TRole extends BuiltSection['role']>(args: {
  role: TRole;
  enabled: boolean;
  required: boolean;
  message: string;
}): BuiltSection<TRole> {
  return {
    role: args.role,
    enabled: args.enabled,
    rendered: false,
    anchorPageIndex: null,
    pages: [],
    templateInputPatch: {},
    issues: [
      {
        code: 'MISSING_MARKER',
        severity: args.required ? 'error' : 'warning',
        message: args.message,
      },
    ],
  };
}

export function createCustomerSectionBuilder(): SectionBuilder {
  return {
    role: 'customer',
    build(context: RenderContext): BuiltSection<'customer'> {
      const page = context.pages.byRole.customer?.[0] ?? null;
      const policy = context.policyBySection.get('customer');

      if (!page) {
        return buildMissingPageSection({
          role: 'customer',
          enabled: policy?.enabled ?? false,
          required: policy?.required ?? true,
          message: 'Customer section page could not be resolved.',
        });
      }

      return {
        role: 'customer',
        enabled: true,
        rendered: true,
        anchorPageIndex: page.pageIndex,
        pages: [cloneTemplate(page.schema)],
        templateInputPatch: {},
        issues: [],
      };
    },
  };
}

export function createProductItemSectionBuilder(): SectionBuilder {
  return {
    role: 'product_items',
    build(context: RenderContext): ProductItemSection {
      const page = context.pages.byRole.product_items?.[0] ?? null;
      const policy = context.policyBySection.get('product_items');
      const built = buildProductItemSectionModel({
        items: context.documentData.items,
        currencyCode: context.documentData.quotation.currencyCode,
      });

      if (!page) {
        return {
          ...buildMissingPageSection({
            role: 'product_items',
            enabled: policy?.enabled ?? false,
            required: policy?.required ?? false,
            message: 'Product item section page could not be resolved.',
          }),
          rows: built.rows,
          tableModel: built.tableModel,
          pagination: built.pagination,
          issues: [
            ...built.issues,
            ...buildMissingPageSection({
              role: 'product_items',
              enabled: policy?.enabled ?? false,
              required: policy?.required ?? false,
              message: 'Product item section page could not be resolved.',
            }).issues,
          ],
        };
      }

      return {
        role: 'product_items',
        enabled: true,
        rendered: true,
        anchorPageIndex: page.pageIndex,
        pages: [cloneTemplate(page.schema)],
        templateInputPatch: {
          items_table: built.tableModel.rows,
        },
        issues: built.issues,
        rows: built.rows,
        tableModel: built.tableModel,
        pagination: built.pagination,
      };
    },
  };
}

export function createTopicSectionBuilder(): SectionBuilder {
  return {
    role: 'topics',
    build(context: RenderContext): BuiltSection<'topics'> {
      const page = context.pages.byRole.topics?.[0] ?? null;
      const policy = context.policyBySection.get('topics');

      if (!page) {
        return buildMissingPageSection({
          role: 'topics',
          enabled: policy?.enabled ?? false,
          required: policy?.required ?? true,
          message: 'Topic section page could not be resolved.',
        });
      }

      const built = buildPdfTopicPages(page.schema, context.documentData.topics.all, {
        basePageIndex: page.pageIndex,
      });

      return {
        role: 'topics',
        enabled: true,
        rendered: true,
        anchorPageIndex: page.pageIndex,
        pages: built.pages,
        templateInputPatch: built.topicInputs,
        issues: built.issues,
      };
    },
  };
}

export function getQuotationSectionBuilders(): SectionBuilder[] {
  return [
    createCustomerSectionBuilder(),
    createProductItemSectionBuilder(),
    createTopicSectionBuilder(),
  ];
}

function createResolvedTemplate(template: ResolvedDocumentTemplate): ResolvedTemplate {
  return {
    documentTemplate: template,
    schema: cloneTemplate(template.version.schemaJson as Template),
  };
}

function createRuntimeErrorMessage(): string {
  return 'Quotation PDF runtime invalid active document template.';
}

export async function buildQuotationPdfRuntime(args: {
  documentData: QuotationDocumentData;
  template: ResolvedDocumentTemplate;
  mappings: DocumentTemplateMappingWithColumns[];
  templateInput: Record<string, unknown>;
}): Promise<QuotationPdfRuntimeResult> {
  const resolvedTemplate = createResolvedTemplate(args.template);
  const compatibleTemplate = adaptTemplateForSectionRuntime(resolvedTemplate);
  const pages = resolveTemplatePages(compatibleTemplate);
  const policies = resolveRenderPolicies({
    pages,
    documentData: args.documentData,
  });
  const staticInputPatch = resolveQuotationStaticTemplateInputs(args.documentData);
  const context = createRenderContext({
    documentData: args.documentData,
    template: resolvedTemplate,
    mappings: args.mappings,
    pages,
    policies,
    issues: pages.issues,
  });
  const composed = await composeSections(context, getQuotationSectionBuilders());
  const assembled = assembleTemplate({
    template: resolvedTemplate.schema,
    templateInput: args.templateInput,
    staticInputPatch,
    sections: composed.sections,
  });
  const issues = [...pages.issues, ...composed.issues, ...assembled.issues];
  const topicSection = composed.sections.find(
    (section): section is BuiltSection<'topics'> => section.role === 'topics',
  );

  if (issues.some((issue) => issue.severity === 'error')) {
    throw new Error(createRuntimeErrorMessage(), { cause: issues });
  }

  return {
    assembled,
    topicInputs: topicSection
      ? (topicSection.templateInputPatch as Record<string, string[][]>)
      : {},
    issues,
    pages,
    policies,
  };
}
