const fs = require('node:fs');
const path = require('node:path');
const postgres = require('postgres');

type SqlClient = ReturnType<typeof postgres>;

type SeedOption = {
  code: string;
  label: string;
  value: string;
  sortOrder: number;
  parentCode?: string;
  parentCategory?: string;
};

type BranchSeedRow = {
  id: string;
  category: string;
  code: string;
  label: string;
};

type TemplateColumnSeed = {
  columnName: string;
  sourceField: string;
  columnLetter?: string;
  sortOrder: number;
  formatMask?: string | null;
};

type TemplateMappingSeed = {
  placeholderKey: string;
  sourcePath: string;
  dataType: 'scalar' | 'multiline' | 'table' | 'image';
  defaultValue?: string | null;
  formatMask?: string | null;
  sortOrder: number;
  columns?: TemplateColumnSeed[];
};

function loadEnvFile(filePath: string) {
  if (!fs.existsSync(filePath)) {
    return;
  }

  const content = fs.readFileSync(filePath, 'utf8');

  for (const rawLine of content.split(/\r?\n/)) {
    const line = rawLine.trim();

    if (!line || line.startsWith('#')) {
      continue;
    }

    const separatorIndex = line.indexOf('=');

    if (separatorIndex === -1) {
      continue;
    }

    const key = line.slice(0, separatorIndex).trim();
    let value = line.slice(separatorIndex + 1).trim();

    if (
      (value.startsWith('"') && value.endsWith('"')) ||
      (value.startsWith("'") && value.endsWith("'"))
    ) {
      value = value.slice(1, -1);
    }

    if (!(key in process.env)) {
      process.env[key] = value;
    }
  }
}

function loadLocalEnv() {
  loadEnvFile(path.resolve(process.cwd(), '.env.local'));
  loadEnvFile(path.resolve(process.cwd(), '.env'));
}

function requireEnv(name: string) {
  const value = process.env[name]?.trim();

  if (!value) {
    throw new Error(`Missing required environment variable: ${name}`);
  }

  return value;
}

function getCurrentPeriod(date = new Date()) {
  const year = String(date.getFullYear()).slice(-2);
  const month = String(date.getMonth() + 1).padStart(2, '0');

  return `${year}${month}`;
}

const FOUNDATION_OPTIONS = {
  crm_branch: [
    { code: 'head_office', label: 'สำนักงานใหญ่', value: 'head_office', sortOrder: 1 },
    { code: 'factory', label: 'Factory', value: 'factory', sortOrder: 2 },
    { code: 'service', label: 'Service', value: 'service', sortOrder: 3 }
  ],
  crm_customer_status: [
    { code: 'active', label: 'Active', value: 'active', sortOrder: 1 },
    { code: 'inactive', label: 'Inactive', value: 'inactive', sortOrder: 2 },
    { code: 'suspended', label: 'Suspended', value: 'suspended', sortOrder: 3 }
  ],
  crm_customer_type: [
    { code: 'company', label: 'Company', value: 'company', sortOrder: 1 },
    { code: 'individual', label: 'Individual', value: 'individual', sortOrder: 2 }
  ],
  crm_customer_group: [
    { code: 'industrial', label: 'Industrial', value: 'industrial', sortOrder: 1 },
    { code: 'construction', label: 'Construction', value: 'construction', sortOrder: 2 },
    { code: 'government', label: 'Government', value: 'government', sortOrder: 3 },
    { code: 'dealer', label: 'Dealer', value: 'dealer', sortOrder: 4 },
    { code: 'other', label: 'Other', value: 'other', sortOrder: 5 }
  ],
  crm_customer_sub_group: [
    {
      code: 'factory',
      label: 'Factory',
      value: 'factory',
      sortOrder: 1,
      parentCode: 'industrial',
      parentCategory: 'crm_customer_group'
    },
    {
      code: 'warehouse',
      label: 'Warehouse',
      value: 'warehouse',
      sortOrder: 2,
      parentCode: 'industrial',
      parentCategory: 'crm_customer_group'
    },
    {
      code: 'logistics',
      label: 'Logistics',
      value: 'logistics',
      sortOrder: 3,
      parentCode: 'industrial',
      parentCategory: 'crm_customer_group'
    },
    {
      code: 'contractor',
      label: 'Contractor',
      value: 'contractor',
      sortOrder: 4,
      parentCode: 'construction',
      parentCategory: 'crm_customer_group'
    },
    {
      code: 'consultant',
      label: 'Consultant',
      value: 'consultant',
      sortOrder: 5,
      parentCode: 'construction',
      parentCategory: 'crm_customer_group'
    },
    {
      code: 'state_enterprise',
      label: 'State Enterprise',
      value: 'state_enterprise',
      sortOrder: 6,
      parentCode: 'government',
      parentCategory: 'crm_customer_group'
    },
    {
      code: 'government_agency',
      label: 'Government Agency',
      value: 'government_agency',
      sortOrder: 7,
      parentCode: 'government',
      parentCategory: 'crm_customer_group'
    },
    {
      code: 'reseller',
      label: 'Reseller',
      value: 'reseller',
      sortOrder: 8,
      parentCode: 'dealer',
      parentCategory: 'crm_customer_group'
    },
    {
      code: 'other',
      label: 'Other',
      value: 'other',
      sortOrder: 9,
      parentCode: 'other',
      parentCategory: 'crm_customer_group'
    }
  ],
  crm_enquiry_status: [
    { code: 'new', label: 'New', value: 'new', sortOrder: 1 },
    { code: 'qualifying', label: 'Qualifying', value: 'qualifying', sortOrder: 2 },
    { code: 'requirement', label: 'Requirement', value: 'requirement', sortOrder: 3 },
    { code: 'follow_up', label: 'Follow Up', value: 'follow_up', sortOrder: 4 },
    { code: 'converted', label: 'Converted', value: 'converted', sortOrder: 5 },
    { code: 'closed_lost', label: 'Closed Lost', value: 'closed_lost', sortOrder: 6 },
    { code: 'cancelled', label: 'Cancelled', value: 'cancelled', sortOrder: 7 }
  ],
  crm_quotation_status: [
    { code: 'draft', label: 'Draft', value: 'draft', sortOrder: 1 },
    {
      code: 'pending_approval',
      label: 'Pending Approval',
      value: 'pending_approval',
      sortOrder: 2
    },
    { code: 'approved', label: 'Approved', value: 'approved', sortOrder: 3 },
    { code: 'rejected', label: 'Rejected', value: 'rejected', sortOrder: 4 },
    { code: 'sent', label: 'Sent', value: 'sent', sortOrder: 5 },
    { code: 'accepted', label: 'Accepted', value: 'accepted', sortOrder: 6 },
    { code: 'lost', label: 'Lost', value: 'lost', sortOrder: 7 },
    { code: 'cancelled', label: 'Cancelled', value: 'cancelled', sortOrder: 8 },
    { code: 'revised', label: 'Revised', value: 'revised', sortOrder: 9 }
  ],
  crm_quotation_type: [
    { code: 'crane', label: 'Crane', value: 'crane', sortOrder: 1 },
    { code: 'dockdoor', label: 'Dock Door', value: 'dockdoor', sortOrder: 2 },
    { code: 'solarcell', label: 'Solar Cell', value: 'solarcell', sortOrder: 3 },
    { code: 'service', label: 'Service', value: 'service', sortOrder: 4 },
    { code: 'other', label: 'Other', value: 'other', sortOrder: 5 }
  ],
  crm_product_type: [
    { code: 'crane', label: 'Crane', value: 'crane', sortOrder: 1 },
    { code: 'dockdoor', label: 'Dock Door', value: 'dockdoor', sortOrder: 2 },
    { code: 'solarcell', label: 'Solar Cell', value: 'solarcell', sortOrder: 3 }
  ],
  crm_currency: [
    { code: 'THB', label: 'THB', value: 'THB', sortOrder: 1 },
    { code: 'USD', label: 'USD', value: 'USD', sortOrder: 2 },
    { code: 'EUR', label: 'EUR', value: 'EUR', sortOrder: 3 }
  ],
  crm_discount_type: [
    { code: 'fixed', label: 'Fixed', value: 'fixed', sortOrder: 1 },
    { code: 'percentage', label: 'Percentage', value: 'percentage', sortOrder: 2 }
  ],
  crm_unit: [
    { code: 'pcs', label: 'PCS', value: 'pcs', sortOrder: 1 },
    { code: 'set', label: 'Set', value: 'set', sortOrder: 2 },
    { code: 'lot', label: 'Lot', value: 'lot', sortOrder: 3 },
    { code: 'job', label: 'Job', value: 'job', sortOrder: 4 }
  ],
  crm_sent_via: [
    { code: 'email', label: 'Email', value: 'email', sortOrder: 1 },
    { code: 'manual', label: 'Manual', value: 'manual', sortOrder: 2 },
    { code: 'system', label: 'System', value: 'system', sortOrder: 3 }
  ],
  crm_project_party_role: [
    {
      code: 'billing_customer',
      label: 'Billing Customer',
      value: 'billing_customer',
      sortOrder: 1
    },
    { code: 'customer', label: 'Customer', value: 'customer', sortOrder: 2 },
    { code: 'consultant', label: 'Consultant', value: 'consultant', sortOrder: 3 },
    { code: 'contractor', label: 'Contractor', value: 'contractor', sortOrder: 4 },
    { code: 'end_customer', label: 'End Customer', value: 'end_customer', sortOrder: 5 },
    { code: 'other', label: 'Other', value: 'other', sortOrder: 6 }
  ],
  crm_quotation_topic_type: [
    { code: 'scope', label: 'Scope', value: 'scope', sortOrder: 1 },
    { code: 'exclusion', label: 'Exclusion', value: 'exclusion', sortOrder: 2 },
    { code: 'payment', label: 'Payment', value: 'payment', sortOrder: 3 }
  ],
  crm_payment_term: [
    { code: 'cash', label: 'Cash', value: 'cash', sortOrder: 1 },
    { code: 'credit_30', label: 'Credit 30', value: 'credit_30', sortOrder: 2 },
    { code: 'credit_60', label: 'Credit 60', value: 'credit_60', sortOrder: 3 }
  ],
  crm_priority: [
    { code: 'low', label: 'Low', value: 'low', sortOrder: 1 },
    { code: 'normal', label: 'Normal', value: 'normal', sortOrder: 2 },
    { code: 'high', label: 'High', value: 'high', sortOrder: 3 },
    { code: 'urgent', label: 'Urgent', value: 'urgent', sortOrder: 4 }
  ],
  crm_followup_type: [
    { code: 'call', label: 'Call', value: 'call', sortOrder: 1 },
    { code: 'meeting', label: 'Meeting', value: 'meeting', sortOrder: 2 },
    { code: 'site_visit', label: 'Site Visit', value: 'site_visit', sortOrder: 3 },
    { code: 'email', label: 'Email', value: 'email', sortOrder: 4 },
    { code: 'line', label: 'LINE', value: 'line', sortOrder: 5 }
  ],
  crm_job_title: [
    { code: 'sales', label: 'Sales Engineer', value: 'sales', sortOrder: 1 },
    { code: 'sales_support', label: 'Sales Support', value: 'sales_support', sortOrder: 2 },
    { code: 'sales_manager', label: 'Sales Manager', value: 'sales_manager', sortOrder: 3 },
    {
      code: 'department_manager',
      label: 'Department Manager',
      value: 'department_manager',
      sortOrder: 4
    },
    { code: 'top_manager', label: 'General Manager', value: 'top_manager', sortOrder: 5 }
  ],
  crm_lead_channel: [
    { code: 'website', label: 'Website', value: 'website', sortOrder: 1 },
    { code: 'referral', label: 'Referral', value: 'referral', sortOrder: 2 },
    { code: 'sales', label: 'Sales', value: 'sales', sortOrder: 3 },
    { code: 'facebook', label: 'Facebook', value: 'facebook', sortOrder: 4 },
    { code: 'line', label: 'LINE', value: 'line', sortOrder: 5 },
    { code: 'other', label: 'Other', value: 'other', sortOrder: 6 }
  ]
};

const DOCUMENT_SEQUENCES = [
  { documentType: 'customer', prefix: 'CUS' },
  { documentType: 'contact', prefix: 'CON' },
  { documentType: 'enquiry', prefix: 'ENQ' },
  { documentType: 'quotation', prefix: 'QT' },
  { documentType: 'approval', prefix: 'APV' }
];

const APPROVAL_WORKFLOWS = [
  {
    code: 'quotation_standard_approval',
    name: 'Quotation Standard Approval',
    entityType: 'quotation',
    steps: [
      { stepNumber: 1, roleCode: 'sales_manager', roleName: 'Sales Manager' },
      { stepNumber: 2, roleCode: 'department_manager', roleName: 'Department Manager' },
      { stepNumber: 3, roleCode: 'top_manager', roleName: 'Top Manager' }
    ]
  }
];

const DOCUMENT_TEMPLATE_MAPPINGS: TemplateMappingSeed[] = [
  {
    placeholderKey: 'customer_name',
    sourcePath: 'customer.name',
    dataType: 'scalar',
    sortOrder: 1
  },
  {
    placeholderKey: 'customer_addr',
    sourcePath: 'customer.address',
    dataType: 'multiline',
    sortOrder: 2
  },
  {
    placeholderKey: 'customer_tel',
    sourcePath: 'customer.phone',
    dataType: 'scalar',
    sortOrder: 3
  },
  {
    placeholderKey: 'customer_email',
    sourcePath: 'customer.email',
    dataType: 'scalar',
    sortOrder: 4
  },
  {
    placeholderKey: 'customer_att',
    sourcePath: 'quotation.attention',
    dataType: 'scalar',
    sortOrder: 5
  },
  {
    placeholderKey: 'project_name',
    sourcePath: 'quotation.projectName',
    dataType: 'scalar',
    sortOrder: 6
  },
  {
    placeholderKey: 'site_location',
    sourcePath: 'quotation.projectLocation',
    dataType: 'multiline',
    sortOrder: 7
  },
  {
    placeholderKey: 'quotation_date',
    sourcePath: 'pdfme.quotation_date',
    dataType: 'scalar',
    formatMask: null,
    sortOrder: 8
  },
  {
    placeholderKey: 'quotation_code',
    sourcePath: 'quotation.code',
    dataType: 'scalar',
    sortOrder: 9
  },
  {
    placeholderKey: 'quotation_price',
    sourcePath: 'pdfme.quotation_price',
    dataType: 'scalar',
    formatMask: null,
    sortOrder: 10
  },
  {
    placeholderKey: 'currency',
    sourcePath: 'quotation.currency',
    dataType: 'scalar',
    sortOrder: 11
  },
  {
    placeholderKey: 'exclusion_data',
    sourcePath: 'pdfme.exclusion_data',
    dataType: 'table',
    sortOrder: 12
  },
  {
    placeholderKey: 'app1',
    sourcePath: 'signatures.preparedBy.name',
    dataType: 'scalar',
    defaultValue: '-',
    sortOrder: 13
  },
  {
    placeholderKey: 'app1_position',
    sourcePath: 'signatures.preparedBy.position',
    dataType: 'scalar',
    defaultValue: '-',
    sortOrder: 14
  },
  {
    placeholderKey: 'app2',
    sourcePath: 'signatures.approvedBy.name',
    dataType: 'scalar',
    defaultValue: '-',
    sortOrder: 15
  },
  {
    placeholderKey: 'app2_position',
    sourcePath: 'signatures.approvedBy.position',
    dataType: 'scalar',
    defaultValue: '-',
    sortOrder: 16
  },
  {
    placeholderKey: 'app3',
    sourcePath: 'signatures.authorizedBy.name',
    dataType: 'scalar',
    defaultValue: '-',
    sortOrder: 17
  },
  {
    placeholderKey: 'app3_position',
    sourcePath: 'signatures.authorizedBy.position',
    dataType: 'scalar',
    defaultValue: '-',
    sortOrder: 18
  },
  {
    placeholderKey: 'items_table',
    sourcePath: 'items',
    dataType: 'table',
    sortOrder: 19,
    columns: [
      { columnName: 'Item', sourceField: 'itemNumber', columnLetter: 'A', sortOrder: 1 },
      { columnName: 'Description', sourceField: 'description', columnLetter: 'B', sortOrder: 2 },
      { columnName: 'Qty', sourceField: 'quantity', columnLetter: 'C', sortOrder: 3 },
      { columnName: 'Unit', sourceField: 'unitLabel', columnLetter: 'D', sortOrder: 4 },
      {
        columnName: 'Unit Price',
        sourceField: 'unitPrice',
        columnLetter: 'E',
        sortOrder: 5,
        formatMask: 'currency'
      },
      {
        columnName: 'Total',
        sourceField: 'totalPrice',
        columnLetter: 'F',
        sortOrder: 6,
        formatMask: 'currency'
      }
    ]
  }
];

function loadTemplateSchemaByOrganizationName(organizationName: string) {
  const normalized = organizationName.toUpperCase();
  const fileName = normalized.includes('ONVALLA')
    ? 'ONVALLA_template_pdfme_fainal3.json'
    : normalized.includes('ALLA')
      ? 'ALLA_template_pdfme_fainal3.json'
      : null;

  if (!fileName) {
    return {
      filePath: null,
      templateName: `${organizationName} Quotation Standard`,
      schemaJson: {
        basePdf: '',
        schemas: [],
        pdfmeVersion: 'fallback'
      }
    };
  }

  const absolutePath = path.resolve(process.cwd(), 'src', 'pdfme_template', fileName);
  const raw = fs.readFileSync(absolutePath, 'utf8');

  return {
    filePath: `src/pdfme_template/${fileName}`,
    templateName: `${organizationName} Quotation Standard`,
    schemaJson: JSON.parse(raw)
  };
}

async function upsertMasterOptionsForOrganization(
  sql: SqlClient,
  organizationId: string
): Promise<BranchSeedRow[]> {
  const branchRows: BranchSeedRow[] = [];

  for (const [category, options] of Object.entries(FOUNDATION_OPTIONS) as Array<
    [string, SeedOption[]]
  >) {
    for (const option of options) {
      const parentRow =
        option.parentCode && option.parentCategory
          ? ((
              await sql`
                select id
                from ms_options
                where organization_id = ${organizationId}
                  and category = ${option.parentCategory}
                  and code = ${option.parentCode}
                  and deleted_at is null
                limit 1
              `
            )[0] ?? null)
          : null;
      const [row] = await sql`
        insert into ms_options (
          id,
          organization_id,
          category,
          code,
          label,
          value,
          parent_id,
          sort_order,
          is_active,
          metadata,
          deleted_at
        ) values (
          ${crypto.randomUUID()},
          ${organizationId},
          ${category},
          ${option.code},
          ${option.label},
          ${option.value},
          ${parentRow?.id ?? null},
          ${option.sortOrder},
          ${true},
          ${null},
          ${null}
        )
        on conflict (organization_id, category, code) do update
        set
          label = excluded.label,
          value = excluded.value,
          parent_id = excluded.parent_id,
          sort_order = excluded.sort_order,
          is_active = excluded.is_active,
          metadata = excluded.metadata,
          deleted_at = excluded.deleted_at,
          updated_at = now()
        returning id, category, code, label
      `;

      if (category === 'crm_branch') {
        branchRows.push(row);
      }
    }
  }

  return branchRows;
}

async function upsertDocumentSequencesForOrganization(
  sql: SqlClient,
  organizationId: string,
  branchRows: BranchSeedRow[]
) {
  if (!branchRows.length) {
    throw new Error(
      `No crm_branch options were found for organization ${organizationId}. Foundation branch seed must run first.`
    );
  }

  const period = getCurrentPeriod();

  for (const branch of branchRows) {
    for (const sequence of DOCUMENT_SEQUENCES) {
      await sql`
        insert into document_sequences (
          id,
          organization_id,
          branch_id,
          document_type,
          prefix,
          period,
          current_number,
          padding_length,
          is_active
        ) values (
          ${crypto.randomUUID()},
          ${organizationId},
          ${branch.id},
          ${sequence.documentType},
          ${sequence.prefix},
          ${period},
          ${0},
          ${3},
          ${true}
        )
        on conflict (organization_id, document_type, period, branch_id) do update
        set
          prefix = excluded.prefix,
          padding_length = excluded.padding_length,
          is_active = excluded.is_active,
          updated_at = now()
      `;
    }
  }
}

async function upsertApprovalWorkflowsForOrganization(sql: SqlClient, organizationId: string) {
  for (const workflow of APPROVAL_WORKFLOWS) {
    const [workflowRow] = await sql`
      insert into crm_approval_workflows (
        id,
        organization_id,
        code,
        name,
        entity_type,
        is_active,
        deleted_at
      ) values (
        ${crypto.randomUUID()},
        ${organizationId},
        ${workflow.code},
        ${workflow.name},
        ${workflow.entityType},
        ${true},
        ${null}
      )
      on conflict (organization_id, code) do update
      set
        name = excluded.name,
        entity_type = excluded.entity_type,
        is_active = excluded.is_active,
        deleted_at = excluded.deleted_at,
        updated_at = now()
      returning id
    `;

    for (const step of workflow.steps) {
      await sql`
        insert into crm_approval_steps (
          id,
          organization_id,
          workflow_id,
          step_number,
          role_code,
          role_name,
          is_required,
          deleted_at
        ) values (
          ${crypto.randomUUID()},
          ${organizationId},
          ${workflowRow.id},
          ${step.stepNumber},
          ${step.roleCode},
          ${step.roleName},
          ${true},
          ${null}
        )
        on conflict (workflow_id, step_number) do update
        set
          role_code = excluded.role_code,
          role_name = excluded.role_name,
          is_required = excluded.is_required,
          deleted_at = excluded.deleted_at,
          updated_at = now()
      `;
    }
  }
}

async function upsertDocumentTemplatesForOrganization(
  sql: SqlClient,
  organization: { id: string; name: string; createdBy: string }
) {
  const templateSeed = loadTemplateSchemaByOrganizationName(organization.name);
  const [templateRow] = await sql`
    insert into crm_document_templates (
      id,
      organization_id,
      document_type,
      product_type,
      file_type,
      template_name,
      description,
      is_default,
      is_active,
      deleted_at,
      created_by,
      updated_by
    ) values (
      ${crypto.randomUUID()},
      ${organization.id},
      ${'quotation'},
      ${'default'},
      ${'pdfme'},
      ${templateSeed.templateName},
      ${'Seeded quotation template metadata for production preview foundation.'},
      ${true},
      ${true},
      ${null},
      ${organization.createdBy},
      ${organization.createdBy}
    )
    on conflict do nothing
    returning id
  `;

  const resolvedTemplateId =
    templateRow?.id ??
    (
      await sql`
        select id
        from crm_document_templates
        where organization_id = ${organization.id}
          and document_type = ${'quotation'}
          and product_type = ${'default'}
          and file_type = ${'pdfme'}
          and deleted_at is null
        order by is_default desc, created_at asc
        limit 1
      `
    )[0]?.id;

  if (!resolvedTemplateId) {
    throw new Error(`Unable to resolve template id for organization ${organization.id}`);
  }

  const [versionRow] = await sql`
    insert into crm_document_template_versions (
      id,
      organization_id,
      template_id,
      version,
      file_path,
      schema_json,
      preview_image_url,
      is_active,
      deleted_at,
      created_by
    ) values (
      ${crypto.randomUUID()},
      ${organization.id},
      ${resolvedTemplateId},
      ${'1.0'},
      ${templateSeed.filePath},
      ${JSON.stringify(templateSeed.schemaJson)},
      ${null},
      ${true},
      ${null},
      ${organization.createdBy}
    )
    on conflict do nothing
    returning id
  `;

  const resolvedVersionId =
    versionRow?.id ??
    (
      await sql`
        select id
        from crm_document_template_versions
        where organization_id = ${organization.id}
          and template_id = ${resolvedTemplateId}
          and version = ${'1.0'}
          and deleted_at is null
        order by created_at asc
        limit 1
      `
    )[0]?.id;

  if (!resolvedVersionId) {
    throw new Error(`Unable to resolve template version id for organization ${organization.id}`);
  }

  for (const mapping of DOCUMENT_TEMPLATE_MAPPINGS) {
    const existingMapping =
      (
        await sql`
          select id
          from crm_document_template_mappings
          where organization_id = ${organization.id}
            and template_version_id = ${resolvedVersionId}
            and placeholder_key = ${mapping.placeholderKey}
          limit 1
        `
      )[0] ?? null;

    const resolvedMappingId = existingMapping?.id ?? crypto.randomUUID();

    if (existingMapping) {
      await sql`
        update crm_document_template_mappings
        set
          source_path = ${mapping.sourcePath},
          data_type = ${mapping.dataType},
          sheet_name = ${null},
          default_value = ${mapping.defaultValue ?? null},
          format_mask = ${mapping.formatMask ?? null},
          sort_order = ${mapping.sortOrder},
          deleted_at = ${null},
          updated_at = now()
        where id = ${resolvedMappingId}
      `;
    } else {
      await sql`
        insert into crm_document_template_mappings (
          id,
          organization_id,
          template_version_id,
          placeholder_key,
          source_path,
          data_type,
          sheet_name,
          default_value,
          format_mask,
          sort_order,
          deleted_at
        ) values (
          ${resolvedMappingId},
          ${organization.id},
          ${resolvedVersionId},
          ${mapping.placeholderKey},
          ${mapping.sourcePath},
          ${mapping.dataType},
          ${null},
          ${mapping.defaultValue ?? null},
          ${mapping.formatMask ?? null},
          ${mapping.sortOrder},
          ${null}
        )
      `;
    }

    if (!resolvedMappingId || !mapping.columns?.length) {
      continue;
    }

    for (const column of mapping.columns) {
      const existingColumn =
        (
          await sql`
            select id
            from crm_document_template_table_columns
            where organization_id = ${organization.id}
              and mapping_id = ${resolvedMappingId}
              and column_name = ${column.columnName}
            limit 1
          `
        )[0] ?? null;

      if (existingColumn) {
        await sql`
          update crm_document_template_table_columns
          set
            source_field = ${column.sourceField},
            column_letter = ${column.columnLetter ?? null},
            sort_order = ${column.sortOrder},
            format_mask = ${column.formatMask ?? null},
            deleted_at = ${null},
            updated_at = now()
          where id = ${existingColumn.id}
        `;
        continue;
      }

      await sql`
        insert into crm_document_template_table_columns (
          id,
          organization_id,
          mapping_id,
          column_name,
          source_field,
          column_letter,
          sort_order,
          format_mask,
          deleted_at
        ) values (
          ${crypto.randomUUID()},
          ${organization.id},
          ${resolvedMappingId},
          ${column.columnName},
          ${column.sourceField},
          ${column.columnLetter ?? null},
          ${column.sortOrder},
          ${column.formatMask ?? null},
          ${null}
        )
      `;
    }
  }

  await sql`
    update crm_document_template_mappings
    set
      deleted_at = now(),
      updated_at = now()
    where organization_id = ${organization.id}
      and template_version_id = ${resolvedVersionId}
      and (
        placeholder_key in (${'topic'}, ${'data_topic'}, ${'item_topic'})
        or placeholder_key like 'topic\_%' escape '\'
        or placeholder_key like 'data_topic\_%' escape '\'
        or placeholder_key like 'item_topic\_%' escape '\'
      )
      and deleted_at is null
  `;
}

async function main() {
  loadLocalEnv();

  const databaseUrl = requireEnv('DATABASE_URL');
  const sql = postgres(databaseUrl, { prepare: false });

  try {
    const organizations = await sql`
      select id, name, created_by as "createdBy"
      from organizations
      order by name asc
    `;

    if (organizations.length === 0) {
      throw new Error(
        'No organizations found. Create at least one organization before running foundation seed.'
      );
    }

    for (const organization of organizations) {
      const branchRows = await upsertMasterOptionsForOrganization(sql, organization.id);
      await upsertDocumentSequencesForOrganization(sql, organization.id, branchRows);
      await upsertApprovalWorkflowsForOrganization(sql, organization.id);
      await upsertDocumentTemplatesForOrganization(sql, organization);
      console.log(`[seed-foundation] Seeded foundation data for ${organization.name}`);
    }
  } finally {
    await sql.end();
  }
}

if (require.main === module) {
  main().catch((error) => {
    console.error('[seed-foundation] Failed:', error.message);
    process.exitCode = 1;
  });
}
