import assert from 'node:assert/strict';
import test from 'node:test';
import type { Template } from '@pdfme/common';
import type { ResolvedDocumentTemplate } from '@/features/foundation/document-template/types';
import type { QuotationDocumentData } from '../types';
import { resolveTemplatePages } from './page-resolver';
import { buildProductItemSectionModel } from './product-item-engine';
import { createRenderContext } from './render-context';
import { createProductItemSectionBuilder, getQuotationSectionBuilders } from './quotation-pdf-runtime';
import type { ProductItemSection, RenderPolicy, ResolvedTemplate } from './runtime-contracts';
import { composeSections } from './section-composer';
import { adaptTemplateForSectionRuntime } from './template-compatibility-adapter';

function createField(name: string, y: number, height = 7) {
  return {
    name,
    type: 'text',
    position: { x: 10, y },
    width: 100,
    height,
    content: `{${name}}`,
  };
}

function createLegacyTemplate(): Template {
  return {
    basePdf: { width: 210, height: 297, padding: [0, 0, 0, 0] },
    schemas: [
      [
        createField('customer_name', 20),
        createField('quotation_price_data', 40, 20),
      ],
      [
        createField('topic', 35),
        createField('data_topic', 45, 7),
        createField('Please_do_not', 225),
        createField('yours_faithfuly', 232),
      ],
    ],
  } as unknown as Template;
}

function createResolvedDocumentTemplate(schema: Template): ResolvedDocumentTemplate {
  return {
    template: {
      id: 'template-1',
      organizationId: 'org-1',
      documentType: 'quotation',
      productType: 'default',
      fileType: 'pdfme',
      templateName: 'Legacy quotation',
      description: null,
      isDefault: true,
      isActive: true,
      createdAt: '2026-06-29T00:00:00.000Z',
      updatedAt: '2026-06-29T00:00:00.000Z',
    },
    version: {
      id: 'template-version-1',
      templateId: 'template-1',
      versionNumber: 1,
      schemaJson: schema,
      createdAt: '2026-06-29T00:00:00.000Z',
      updatedAt: '2026-06-29T00:00:00.000Z',
    },
  } as unknown as ResolvedDocumentTemplate;
}

function createItem(
  index: number,
  overrides: Partial<QuotationDocumentData['items'][number]> = {},
): QuotationDocumentData['items'][number] {
  return {
    id: `item-${index}`,
    itemNumber: index + 1,
    productTypeCode: null,
    productTypeLabel: null,
    description: `Product item ${index + 1}`,
    quantity: 1,
    unitCode: 'EA',
    unitLabel: 'EA',
    unitPrice: 1000 + index,
    discount: 0,
    discountTypeCode: null,
    taxRate: 0,
    totalPrice: 1000 + index,
    notes: null,
    ...overrides,
  };
}

function createDocumentData(items: QuotationDocumentData['items']): QuotationDocumentData {
  return {
    company: {
      id: 'company-1',
      name: 'ALLA',
      slug: 'alla',
      imageUrl: null,
      plan: 'pro',
    },
    branch: null,
    customer: {
      id: 'customer-1',
      code: 'C-001',
      name: 'Customer',
      address: null,
      phone: null,
      email: null,
      taxId: null,
    },
    contact: null,
    quotation: {
      id: 'quotation-1',
      code: 'QT-TEST',
      quotationDate: '2026-06-29',
      validUntil: null,
      projectName: null,
      projectLocation: null,
      attention: null,
      reference: null,
      revision: 0,
      revisionLabel: 'Rev.0',
      statusCode: 'approved',
      statusLabel: 'Approved',
      quotationTypeCode: null,
      quotationTypeLabel: null,
      currency: 'THB',
      currencyCode: 'THB',
      currencyLabel: 'Thai Baht',
      exchangeRate: 1,
      subtotal: 0,
      discount: 0,
      discountTypeCode: null,
      discountTypeLabel: null,
      taxRate: 0,
      taxAmount: 0,
      totalAmount: 0,
      notes: null,
    },
    items,
    quotationCustomers: [],
    topics: {
      scope: [],
      exclusion: [],
      payment: [],
      warranty: [],
      delivery: [],
      other: [],
      all: [],
    },
    pdfme: {
      labels: {
        tel: 'Tel',
        email: 'Email',
        att: 'Att',
        project: 'Project',
        location: 'Location',
      },
      quotation_date: '2026-06-29',
      quotation_price: 'THB 0.00',
      quotation_price_data: [],
      exclusion_data: [],
      topic_inputs: {},
    },
    approval: {
      requestId: null,
      workflowName: null,
      status: 'approved',
      approvedAt: '2026-06-29T00:00:00.000Z',
      currentStep: null,
      currentStepRoleName: null,
      approvers: [],
    },
    signatures: {
      preparedBy: { name: 'Prepared', position: 'Sales' },
      approvedBy: { name: 'Approved', position: 'Manager' },
      authorizedBy: { name: 'Authorized', position: 'Director' },
    },
    watermarkStatus: null,
  };
}

function createRuntimeContext(items: QuotationDocumentData['items']) {
  const schema = createLegacyTemplate();
  const template = createResolvedDocumentTemplate(schema);
  const runtimeTemplate: ResolvedTemplate = {
    documentTemplate: template,
    schema,
  };
  const pages = resolveTemplatePages(adaptTemplateForSectionRuntime(runtimeTemplate));
  const policies: RenderPolicy[] = [
    { section: 'customer', enabled: true, required: true, visibleWhenEmpty: true },
    { section: 'product_items', enabled: true, required: false, visibleWhenEmpty: false },
    { section: 'topics', enabled: true, required: true, visibleWhenEmpty: true },
    { section: 'conditions', enabled: false, required: false, visibleWhenEmpty: false },
    { section: 'signature', enabled: false, required: false, visibleWhenEmpty: false },
    { section: 'attachments', enabled: false, required: false, visibleWhenEmpty: false },
    { section: 'appendix', enabled: false, required: false, visibleWhenEmpty: false },
    { section: 'warranty', enabled: false, required: false, visibleWhenEmpty: false },
    { section: 'cover', enabled: false, required: false, visibleWhenEmpty: false },
  ];

  return createRenderContext({
    documentData: createDocumentData(items),
    template: runtimeTemplate,
    mappings: [],
    pages,
    policies,
    issues: pages.issues,
  });
}

test('product item engine returns empty section model when quotation has no items', () => {
  const built = buildProductItemSectionModel({ items: [], currencyCode: 'THB' });

  assert.equal(built.rows.length, 0);
  assert.deepEqual(built.tableModel.headers, [
    'Item',
    'Description',
    'Qty',
    'Unit',
    'Unit Price',
    'Discount',
    'Total',
  ]);
  assert.equal(built.pagination.estimatedPages, 0);
  assert.deepEqual(built.issues, []);
});

test('product item engine formats one row with decimal quantity and discount', () => {
  const built = buildProductItemSectionModel({
    items: [
      createItem(0, {
        quantity: 2.5,
        unitPrice: 1234.5,
        discount: 50,
        totalPrice: 3036.25,
      }),
    ],
    currencyCode: 'THB',
  });

  assert.equal(built.rows.length, 1);
  assert.equal(built.rows[0]?.quantity, '2.5');
  assert.equal(built.rows[0]?.unitPrice, 'THB 1,234.50');
  assert.equal(built.rows[0]?.discount, 'THB 50.00');
  assert.equal(built.rows[0]?.total, 'THB 3,036.25');
  assert.deepEqual(built.tableModel.rows[0], [
    '1',
    'Product item 1',
    '2.5',
    'EA',
    'THB 1,234.50',
    'THB 50.00',
    'THB 3,036.25',
  ]);
});

test('product item engine supports 10 and 100 items with pagination metadata', () => {
  const tenItems = buildProductItemSectionModel({
    items: Array.from({ length: 10 }, (_, index) => createItem(index)),
    currencyCode: 'THB',
  });
  const hundredItems = buildProductItemSectionModel({
    items: Array.from({ length: 100 }, (_, index) => createItem(index)),
    currencyCode: 'THB',
  });

  assert.equal(tenItems.rows.length, 10);
  assert.equal(tenItems.pagination.rowCount, 10);
  assert.equal(tenItems.pagination.estimatedPages, 1);

  assert.equal(hundredItems.rows.length, 100);
  assert.ok(hundredItems.pagination.estimatedPages > 1);
  assert.equal(
    hundredItems.pagination.rowsPerPage.reduce((sum, value) => sum + value, 0),
    100,
  );
});

test('product item engine increases estimated row height for long descriptions and allows missing optionals', () => {
  const longDescription = Array.from({ length: 40 }, () => 'long-description').join(' ');
  const built = buildProductItemSectionModel({
    items: [
      createItem(0, {
        description: longDescription,
        unitLabel: null,
        notes: 'extra note',
      }),
    ],
    currencyCode: 'THB',
  });

  assert.ok((built.rows[0]?.estimatedLineCount ?? 0) > 1);
  assert.ok((built.rows[0]?.estimatedRowHeight ?? 0) > 24);
  assert.equal(built.rows[0]?.unit, '-');
  assert.match(built.rows[0]?.description ?? '', /extra note/);
});

test('product item engine reports invalid quantity, price, description, and total as runtime issues', () => {
  const built = buildProductItemSectionModel({
    items: [
      createItem(0, {
        description: '',
        quantity: Number.NaN,
        unitPrice: Number.POSITIVE_INFINITY,
        totalPrice: Number.NaN,
      }),
    ],
    currencyCode: 'THB',
  });

  assert.deepEqual(
    built.issues.map((issue) => issue.code).sort(),
    [
      'INVALID_PRODUCT_ITEM_PRICE',
      'INVALID_PRODUCT_ITEM_QUANTITY',
      'INVALID_PRODUCT_ITEM_TOTAL',
      'MISSING_PRODUCT_ITEM_DESCRIPTION',
    ],
  );
});

test('section composer accepts product item builder and existing sections remain available', async () => {
  const context = createRuntimeContext([
    createItem(0),
    createItem(1, { discount: 25, totalPrice: 1976 }),
  ]);

  const section = (await createProductItemSectionBuilder().build(context)) as ProductItemSection;
  assert.equal(section.rendered, false);
  assert.equal(section.anchorPageIndex, null);
  assert.equal(section.rows.length, 2);

  const composed = await composeSections(context, getQuotationSectionBuilders());
  const sectionRoles = composed.sections.map((item) => item.role);

  assert.deepEqual(sectionRoles, ['customer', 'product_items', 'topics']);
  const productItemsSection = composed.sections.find(
    (item): item is ProductItemSection => item.role === 'product_items',
  );
  assert.ok(productItemsSection);
  assert.equal(productItemsSection.tableModel.rows.length, 2);
  assert.ok(composed.sections.some((item) => item.role === 'customer'));
  assert.ok(composed.sections.some((item) => item.role === 'topics'));
});
