const fs = require("node:fs");
const path = require("node:path");
const { createHash } = require("node:crypto");
const { PDFDocument, StandardFonts, rgb } = require("@pdfme/pdf-lib");
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[];
};

type ReportDefinitionSeed = {
  code: string;
  name: string;
  description: string;
  category: string;
};

type DocumentLibraryVersionSeed = {
  version: string;
  status: "draft" | "published" | "active" | "archived";
  fileName: string;
  summary: string;
};

type DocumentLibrarySeed = {
  code: string;
  name: string;
  documentType: string;
  description: string;
  brand: string;
  language: string;
  productType: string;
  versions: DocumentLibraryVersionSeed[];
};

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,
    },
  ],
  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_opportunity_status: [
    { code: "new", label: "New", value: "new", sortOrder: 1 },
    {
      code: "qualification",
      label: "Qualification",
      value: "qualification",
      sortOrder: 2,
    },
    {
      code: "requirement_gathering",
      label: "Requirement Gathering",
      value: "requirement_gathering",
      sortOrder: 3,
    },
    {
      code: "site_survey",
      label: "Site Survey",
      value: "site_survey",
      sortOrder: 4,
    },
    {
      code: "proposal_preparation",
      label: "Proposal Preparation",
      value: "proposal_preparation",
      sortOrder: 5,
    },
    {
      code: "quotation_created",
      label: "Quotation Created",
      value: "quotation_created",
      sortOrder: 6,
    },
    {
      code: "negotiation",
      label: "Negotiation",
      value: "negotiation",
      sortOrder: 7,
    },
    { code: "closed", label: "Closed", value: "closed", sortOrder: 8 },
  ],
  crm_opportunity_stage: [
    { code: "new", label: "New", value: "new", sortOrder: 1 },
    {
      code: "qualification",
      label: "Qualification",
      value: "qualification",
      sortOrder: 2,
    },
    {
      code: "requirement_gathering",
      label: "Requirement Gathering",
      value: "requirement_gathering",
      sortOrder: 3,
    },
    {
      code: "site_survey",
      label: "Site Survey",
      value: "site_survey",
      sortOrder: 4,
    },
    {
      code: "proposal_preparation",
      label: "Proposal Preparation",
      value: "proposal_preparation",
      sortOrder: 5,
    },
    {
      code: "quotation_created",
      label: "Quotation Created",
      value: "quotation_created",
      sortOrder: 6,
    },
    {
      code: "negotiation",
      label: "Negotiation",
      value: "negotiation",
      sortOrder: 7,
    },
    { code: "closed", label: "Closed", value: "closed", sortOrder: 8 },
  ],
  crm_opportunity_outcome_status: [
    { code: "open", label: "Open", value: "open", sortOrder: 1 },
    { code: "won", label: "Won", value: "won", sortOrder: 2 },
    { code: "lost", label: "Lost", value: "lost", sortOrder: 3 },
    { code: "cancelled", label: "Cancelled", value: "cancelled", sortOrder: 4 },
    {
      code: "no_quotation",
      label: "No Quotation",
      value: "no_quotation",
      sortOrder: 5,
    },
  ],
  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,
    },
    { code: "service", label: "Service", value: "service", sortOrder: 4 },
    {
      code: "sparepart",
      label: "Spare Part",
      value: "sparepart",
      sortOrder: 5,
    },
  ],
  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_lost_reason: [
    {
      code: "competitor_won",
      label: "Competitor Won",
      value: "competitor_won",
      sortOrder: 1,
    },
    {
      code: "budget_cancelled",
      label: "Budget Cancelled",
      value: "budget_cancelled",
      sortOrder: 2,
    },
    {
      code: "timeline_not_match",
      label: "Timeline Not Match",
      value: "timeline_not_match",
      sortOrder: 3,
    },
    {
      code: "spec_not_match",
      label: "Spec Not Match",
      value: "spec_not_match",
      sortOrder: 4,
    },
    {
      code: "customer_no_response",
      label: "Customer No Response",
      value: "customer_no_response",
      sortOrder: 5,
    },
    {
      code: "price_too_high",
      label: "Price Too High",
      value: "price_too_high",
      sortOrder: 6,
    },
    {
      code: "timeline_not_fit",
      label: "Timeline Not Fit",
      value: "timeline_not_fit",
      sortOrder: 7,
    },
    { code: "other", label: "Other", value: "other", sortOrder: 8 },
  ],
  crm_opportunity_lost_reason: [
    {
      code: "competitor_won",
      label: "Competitor Won",
      value: "competitor_won",
      sortOrder: 1,
    },
    {
      code: "budget_cancelled",
      label: "Budget Cancelled",
      value: "budget_cancelled",
      sortOrder: 2,
    },
    {
      code: "timeline_not_match",
      label: "Timeline Not Match",
      value: "timeline_not_match",
      sortOrder: 3,
    },
    {
      code: "spec_not_match",
      label: "Spec Not Match",
      value: "spec_not_match",
      sortOrder: 4,
    },
    {
      code: "customer_no_response",
      label: "Customer No Response",
      value: "customer_no_response",
      sortOrder: 5,
    },
    {
      code: "price_too_high",
      label: "Price Too High",
      value: "price_too_high",
      sortOrder: 6,
    },
    {
      code: "timeline_not_fit",
      label: "Timeline Not Fit",
      value: "timeline_not_fit",
      sortOrder: 7,
    },
    { code: "other", label: "Other", value: "other", sortOrder: 8 },
  ],
  crm_opportunity_cancel_reason: [
    {
      code: "customer_cancelled",
      label: "Customer Cancelled",
      value: "customer_cancelled",
      sortOrder: 1,
    },
    {
      code: "internal_hold",
      label: "Internal Hold",
      value: "internal_hold",
      sortOrder: 2,
    },
    {
      code: "scope_changed",
      label: "Scope Changed",
      value: "scope_changed",
      sortOrder: 3,
    },
    { code: "other", label: "Other", value: "other", sortOrder: 4 },
  ],
  crm_opportunity_no_quotation_reason: [
    {
      code: "budget_not_ready",
      label: "Budget Not Ready",
      value: "budget_not_ready",
      sortOrder: 1,
    },
    {
      code: "qualification_failed",
      label: "Qualification Failed",
      value: "qualification_failed",
      sortOrder: 2,
    },
    {
      code: "duplicate_opportunity",
      label: "Duplicate Opportunity",
      value: "duplicate_opportunity",
      sortOrder: 3,
    },
    {
      code: "customer_stopped",
      label: "Customer Stopped",
      value: "customer_stopped",
      sortOrder: 4,
    },
    { code: "other", label: "Other", value: "other", 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 },
  ],
  crm_lead_awareness: [
    {
      code: "google_search_website",
      label: "Google Search / Website",
      value: "google_search_website",
      sortOrder: 1,
    },
    {
      code: "social_media",
      label: "Social Media",
      value: "social_media",
      sortOrder: 2,
    },
    {
      code: "email_marketing",
      label: "Email Marketing",
      value: "email_marketing",
      sortOrder: 3,
    },
    { code: "visit", label: "Visit", value: "visit", sortOrder: 4 },
    {
      code: "exhibition_event",
      label: "Exhibition / Event",
      value: "exhibition_event",
      sortOrder: 5,
    },
    {
      code: "brochure_catalog",
      label: "Brochure / Catalog",
      value: "brochure_catalog",
      sortOrder: 6,
    },
    {
      code: "word_of_mouth",
      label: "Word of Mouth",
      value: "word_of_mouth",
      sortOrder: 7,
    },
    {
      code: "corporate_car",
      label: "Corporate Car",
      value: "corporate_car",
      sortOrder: 8,
    },
    {
      code: "product_label",
      label: "Product Label",
      value: "product_label",
      sortOrder: 9,
    },
    {
      code: "repurchasing",
      label: "Re-purchasing",
      value: "repurchasing",
      sortOrder: 10,
    },
    {
      code: "introducing",
      label: "Introducing",
      value: "introducing",
      sortOrder: 11,
    },
    { code: "bci", label: "BCI", value: "bci", sortOrder: 12 },
  ],
  crm_lead_status: [
    { code: "new_job", label: "New Job", value: "new_job", sortOrder: 1 },
    { code: "follow_up", label: "Follow Up", value: "follow_up", sortOrder: 2 },
    { code: "assigned", label: "Assigned", value: "assigned", sortOrder: 3 },
    {
      code: "closed_lost",
      label: "Closed Lost",
      value: "closed_lost",
      sortOrder: 4,
    },
    { code: "cancel", label: "Cancel", value: "cancel", sortOrder: 5 },
  ],
  crm_lead_followup_status: [
    {
      code: "budget_pending",
      label: "Budget Pending",
      value: "budget_pending",
      sortOrder: 1,
    },
    {
      code: "project_under_review",
      label: "Project Under Review",
      value: "project_under_review",
      sortOrder: 2,
    },
    {
      code: "spec_clarification",
      label: "Spec Clarification",
      value: "spec_clarification",
      sortOrder: 3,
    },
    {
      code: "site_survey_waiting",
      label: "Site Survey Waiting",
      value: "site_survey_waiting",
      sortOrder: 4,
    },
    {
      code: "drawing_followup",
      label: "Drawing Follow-up",
      value: "drawing_followup",
      sortOrder: 5,
    },
    {
      code: "quotation_preparation",
      label: "Quotation Preparation",
      value: "quotation_preparation",
      sortOrder: 6,
    },
    {
      code: "waiting_po",
      label: "Waiting PO",
      value: "waiting_po",
      sortOrder: 7,
    },
  ],
  crm_lead_lost_reason: [
    { code: "price", label: "Price", value: "price", sortOrder: 1 },
    {
      code: "delivery_time",
      label: "Delivery Time",
      value: "delivery_time",
      sortOrder: 2,
    },
    {
      code: "sales_person",
      label: "Sales Person",
      value: "sales_person",
      sortOrder: 3,
    },
    { code: "service", label: "Service", value: "service", sortOrder: 4 },
    { code: "customer", label: "Customer", value: "customer", sortOrder: 5 },
    {
      code: "product_quality",
      label: "Product Quality",
      value: "product_quality",
      sortOrder: 6,
    },
    { code: "other", label: "Other", value: "other", sortOrder: 7 },
    { code: "transfer", label: "Transfer", value: "transfer", sortOrder: 8 },
  ],
  crm_report_category: [
    { code: "pipeline", label: "Pipeline", value: "pipeline", sortOrder: 1 },
    { code: "sales", label: "Sales", value: "sales", sortOrder: 2 },
    { code: "revenue", label: "Revenue", value: "revenue", sortOrder: 3 },
    { code: "customer", label: "Customer", value: "customer", sortOrder: 4 },
    { code: "quotation", label: "Quotation", value: "quotation", sortOrder: 5 },
    { code: "approval", label: "Approval", value: "approval", sortOrder: 6 },
    { code: "outcome", label: "Outcome", value: "outcome", sortOrder: 7 },
    { code: "executive", label: "Executive", value: "executive", sortOrder: 8 },
  ],
};

const REPORT_DEFINITIONS: ReportDefinitionSeed[] = [
  {
    code: "lead_pipeline",
    name: "Lead Pipeline",
    description:
      "Operational report for marketing lead intake, assignment, and conversion status.",
    category: "pipeline",
  },
  {
    code: "lead_aging",
    name: "Lead Aging",
    description:
      "Operational report for stagnant lead monitoring and follow-up prioritization.",
    category: "pipeline",
  },
  {
    code: "opportunity_pipeline",
    name: "Opportunity Pipeline",
    description:
      "Operational report for open opportunities, quotation progress, and sales pipeline health.",
    category: "pipeline",
  },
  {
    code: "opportunity_aging",
    name: "Opportunity Aging",
    description:
      "Operational report for stalled opportunities and aging opportunity follow-ups.",
    category: "pipeline",
  },
  {
    code: "pipeline_funnel",
    name: "Pipeline Funnel",
    description:
      "Lifecycle funnel from lead through quotation to won and lost outcomes.",
    category: "pipeline",
  },
  {
    code: "lead_conversion",
    name: "Lead Conversion",
    description:
      "Marketing conversion report from lead creation into active opportunities.",
    category: "pipeline",
  },
  {
    code: "opportunity_conversion",
    name: "Opportunity Conversion",
    description:
      "Sales conversion report from opportunity creation into quotation generation.",
    category: "pipeline",
  },
  {
    code: "sales_performance",
    name: "Sales Performance",
    description:
      "Foundation catalog entry for salesperson KPI and ranking reports.",
    category: "sales",
  },
  {
    code: "lost_analysis",
    name: "Lost Analysis",
    description:
      "Foundation catalog entry for lost reason and competitor analysis.",
    category: "outcome",
  },
  {
    code: "revenue_by_customer",
    name: "Revenue By Customer",
    description:
      "Foundation catalog entry for customer revenue attribution reports.",
    category: "revenue",
  },
];

const DOCUMENT_LIBRARY_SEEDS: DocumentLibrarySeed[] = [
  {
    code: "SLA_ALLA_CRANE_TH",
    name: "SLA - ALLA - Crane - TH",
    documentType: "sla",
    description:
      "Seeded service level agreement pack for ALLA crane quotations.",
    brand: "alla",
    language: "th",
    productType: "crane",
    versions: [
      {
        version: "1.0",
        status: "archived",
        fileName: "sla-alla-crane-th-v1.0.pdf",
        summary: "Archived baseline SLA kept for historical reference.",
      },
      {
        version: "1.1",
        status: "active",
        fileName: "sla-alla-crane-th-v1.1.pdf",
        summary:
          "Current active SLA version used as the default append document.",
      },
    ],
  },
  {
    code: "WARRANTY_ONVALLA_DOCK_DOOR_EN",
    name: "Warranty - ONVALLA - Dock Door - EN",
    documentType: "warranty",
    description: "Seeded warranty booklet for ONVALLA dock door opportunities.",
    brand: "onvalla",
    language: "en",
    productType: "dock_door",
    versions: [
      {
        version: "1.0",
        status: "active",
        fileName: "warranty-onvalla-dock-door-en-v1.0.pdf",
        summary: "Active warranty issue for English dock door quotations.",
      },
    ],
  },
  {
    code: "COMPANY_PROFILE_ALLA_ALL_TH",
    name: "Company Profile - ALLA - All Product Types - TH",
    documentType: "company_profile",
    description:
      "Seeded company profile for Thai proposals across all ALLA product families.",
    brand: "alla",
    language: "th",
    productType: "all",
    versions: [
      {
        version: "2025.12",
        status: "active",
        fileName: "company-profile-alla-all-th-2025.12.pdf",
        summary:
          "Current active company profile packaged with selected quotations.",
      },
      {
        version: "2026.01",
        status: "published",
        fileName: "company-profile-alla-all-th-2026.01.pdf",
        summary: "Published refresh ready for operator activation review.",
      },
    ],
  },
  {
    code: "INSTALLATION_MANUAL_GENERIC_SERVICE_EN",
    name: "Installation Manual - Generic - Service - EN",
    documentType: "installation_manual",
    description:
      "Seeded draft service installation manual for future operator review.",
    brand: "generic",
    language: "en",
    productType: "service",
    versions: [
      {
        version: "0.9-draft",
        status: "draft",
        fileName: "installation-manual-generic-service-en-v0.9-draft.pdf",
        summary:
          "Draft-only technical manual left unpublished to exercise lifecycle controls.",
      },
    ],
  },
];

function sha256(buffer: Buffer) {
  return createHash("sha256").update(buffer).digest("hex");
}

function sanitizeStorageSegment(value: string) {
  return value
    .trim()
    .replace(/\\/g, "/")
    .replace(/^\/+|\/+$/g, "")
    .replace(/[^a-zA-Z0-9._/-]+/g, "-");
}

function buildOrganizationStorageKey(
  organizationId: string,
  segments: string[],
) {
  return [
    "organizations",
    organizationId,
    ...segments.map(sanitizeStorageSegment).filter(Boolean),
  ].join("/");
}

function resolveLifecycleTimestamps(
  status: DocumentLibraryVersionSeed["status"],
) {
  switch (status) {
    case "draft":
      return {
        publishedAt: null,
        archivedAt: null,
      };
    case "published":
      return {
        publishedAt: new Date("2026-01-20T02:00:00.000Z"),
        archivedAt: null,
      };
    case "active":
      return {
        publishedAt: new Date("2026-01-10T02:00:00.000Z"),
        archivedAt: null,
      };
    case "archived":
      return {
        publishedAt: new Date("2025-12-15T02:00:00.000Z"),
        archivedAt: new Date("2026-01-05T02:00:00.000Z"),
      };
    default:
      return {
        publishedAt: null,
        archivedAt: null,
      };
  }
}

async function createDocumentLibrarySeedPdf(input: {
  organizationName: string;
  libraryName: string;
  documentType: string;
  brand: string;
  language: string;
  productType: string;
  version: string;
  summary: string;
}) {
  const pdf = await PDFDocument.create();
  const page = pdf.addPage([595.28, 841.89]);
  const titleFont = await pdf.embedFont(StandardFonts.HelveticaBold);
  const bodyFont = await pdf.embedFont(StandardFonts.Helvetica);

  let cursorY = 780;

  page.drawText(input.libraryName, {
    x: 48,
    y: cursorY,
    size: 18,
    font: titleFont,
    color: rgb(0.1, 0.16, 0.24),
  });

  cursorY -= 32;

  const lines = [
    `Organization: ${input.organizationName}`,
    `Document Type: ${input.documentType}`,
    `Brand: ${input.brand}`,
    `Language: ${input.language}`,
    `Product Type: ${input.productType}`,
    `Version: ${input.version}`,
    "",
    input.summary,
    "",
    "Seeded by foundation.seed.ts for Document Library management verification.",
  ];

  for (const line of lines) {
    page.drawText(line, {
      x: 48,
      y: cursorY,
      size: line === input.summary ? 12 : 11,
      font: line === input.summary ? titleFont : bodyFont,
      color: rgb(0.23, 0.27, 0.33),
    });

    cursorY -= line === "" ? 18 : 20;
  }

  const bytes = await pdf.save();

  return Buffer.from(bytes);
}

async function getSeedStorageProvider() {
  const storageModule =
    await import("../../features/foundation/storage/server/provider.ts");
  return storageModule.resolveStorageProvider();
}

async function getDocumentSequenceConfig() {
  return import("../../features/foundation/document-sequence/config.ts");
}

const DOCUMENT_SEQUENCES = [
  { documentType: "customer", prefix: "CUS" },
  { documentType: "contact", prefix: "CON" },
  { documentType: "opportunity", prefix: "ENQ" },
  { documentType: "crm_lead", prefix: "LD" },
  { documentType: "crm_opportunity", prefix: "EN" },
  { documentType: "approval", prefix: "APV" },
];

const QUOTATION_SEQUENCE_PRODUCT_TYPES = [
  "crane",
  "dockdoor",
  "solarcell",
  "service",
  "sparepart",
];

const APPROVAL_WORKFLOWS = [
  {
    code: "quotation_standard_approval",
    name: "Quotation Standard Approval",
    entityType: "quotation",
    description: "Default sequential quotation approval workflow",
    isSystem: true,
    steps: [
      {
        stepNumber: 1,
        roleCode: "sales_manager",
        roleName: "Sales Manager",
        approvalMode: "sequential",
      },
      {
        stepNumber: 2,
        roleCode: "department_manager",
        roleName: "Department Manager",
        approvalMode: "sequential",
      },
      {
        stepNumber: 3,
        roleCode: "top_manager",
        roleName: "Top Manager",
        approvalMode: "sequential",
      },
    ],
  },
];

const APPROVAL_MATRIX_DEFAULTS = [
  {
    entityType: "quotation",
    workflowCode: "quotation_standard_approval",
    branchId: null,
    productType: null,
    minAmount: null,
    maxAmount: null,
    currency: null,
    priority: 999,
    isDefault: true,
    isActive: true,
    description: "Default quotation approval matrix",
  },
];

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_data",
    sourcePath: "pdfme.quotation_price_data",
    dataType: "table",
    defaultValue: JSON.stringify([["Price (Exclude VAT)", "-", " ", "THB"]]),
    sortOrder: 10,
  },
  {
    placeholderKey: "quotation_price",
    sourcePath: "pdfme.quotation_price",
    dataType: "scalar",
    formatMask: null,
    sortOrder: 11,
  },
  {
    placeholderKey: "currency",
    sourcePath: "quotation.currency",
    dataType: "scalar",
    sortOrder: 12,
  },
  {
    placeholderKey: "exclusion_data",
    sourcePath: "pdfme.exclusion_data",
    dataType: "table",
    sortOrder: 13,
  },
  {
    placeholderKey: "app1",
    sourcePath: "signatures.preparedBy.name",
    dataType: "scalar",
    defaultValue: "-",
    sortOrder: 14,
  },
  {
    placeholderKey: "app1_position",
    sourcePath: "signatures.preparedBy.position",
    dataType: "scalar",
    defaultValue: "-",
    sortOrder: 15,
  },
  {
    placeholderKey: "app2",
    sourcePath: "signatures.approvedBy.name",
    dataType: "scalar",
    defaultValue: "-",
    sortOrder: 16,
  },
  {
    placeholderKey: "app2_position",
    sourcePath: "signatures.approvedBy.position",
    dataType: "scalar",
    defaultValue: "-",
    sortOrder: 17,
  },
  {
    placeholderKey: "app3",
    sourcePath: "signatures.authorizedBy.name",
    dataType: "scalar",
    defaultValue: "-",
    sortOrder: 18,
  },
  {
    placeholderKey: "app3_position",
    sourcePath: "signatures.authorizedBy.position",
    dataType: "scalar",
    defaultValue: "-",
    sortOrder: 19,
  },
  {
    placeholderKey: "items_table",
    sourcePath: "items",
    dataType: "table",
    sortOrder: 20,
    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 upsertDocumentSequencesForOrganizationV2(
  sql: SqlClient,
  organization: { id: string; slug: string },
  branchRows: BranchSeedRow[],
) {
  if (!branchRows.length) {
    throw new Error(
      `No crm_branch options found for organization ${organization.id}. Foundation branch seed must run first.`,
    );
  }

  const period = getCurrentPeriod();
  const sequenceConfig = await getDocumentSequenceConfig();
  const organizationCode =
    sequenceConfig.resolveDocumentSequenceOrganizationCode(organization.slug);

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

    if (!organizationCode) {
      continue;
    }

    for (const productType of QUOTATION_SEQUENCE_PRODUCT_TYPES) {
      const productTypeCode =
        sequenceConfig.resolveDocumentSequenceProductTypeCode(productType);

      if (!productTypeCode) {
        continue;
      }

      await sql`
        insert into document_sequences (
          id,
          organization_id,
          branch_id,
          product_type,
          document_type,
          prefix,
          period,
          current_number,
          padding_length,
          format,
          reset_policy,
          is_active
        ) values (
          ${crypto.randomUUID()},
          ${organization.id},
          ${branch.id},
          ${productType},
          ${"quotation"},
          ${`${productTypeCode}${organizationCode}`},
          ${period},
          ${0},
          ${3},
          ${"{prefix}{period}-{running}"},
          ${"period"},
          ${true}
        )
        on conflict (organization_id, branch_id, product_type, document_type, period) do update
        set
          prefix = excluded.prefix,
          padding_length = excluded.padding_length,
          format = excluded.format,
          reset_policy = excluded.reset_policy,
          is_active = excluded.is_active,
          updated_at = now()
      `;
    }
  }
}

async function upsertApprovalWorkflowsForOrganization(
  sql: SqlClient,
  organization: { id: string; createdBy: 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,
        created_by,
        updated_by,
        deleted_at
      ) values (
        ${crypto.randomUUID()},
        ${organization.id},
        ${workflow.code},
        ${workflow.name},
        ${workflow.entityType},
        ${true},
        ${organization.createdBy},
        ${organization.createdBy},
        ${null}
      )
      on conflict (organization_id, code) do update
      set
        name = excluded.name,
        entity_type = excluded.entity_type,
        is_active = excluded.is_active,
        updated_by = excluded.updated_by,
        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()},
          ${organization.id},
          ${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 upsertApprovalMatricesForOrganization(
  sql: SqlClient,
  organization: { id: string; createdBy: string },
) {
  const workflows = await sql`
    select id, code
    from crm_approval_workflows
    where organization_id = ${organization.id}
      and deleted_at is null
  `;

  const workflowIdByCode = new Map(
    workflows.map((workflow: { code: string; id: string }) => [
      workflow.code,
      workflow.id,
    ]),
  );

  for (const matrix of APPROVAL_MATRIX_DEFAULTS) {
    const workflowId = workflowIdByCode.get(matrix.workflowCode);
    if (!workflowId) {
      continue;
    }

    const [existing] = await sql`
      select id
      from crm_approval_matrices
      where organization_id = ${organization.id}
        and entity_type = ${matrix.entityType}
        and is_default = ${matrix.isDefault}
        and deleted_at is null
      order by created_at asc
      limit 1
    `;

    if (existing?.id) {
      await sql`
        update crm_approval_matrices
        set workflow_id = ${workflowId},
            branch_id = ${matrix.branchId},
            product_type = ${matrix.productType},
            min_amount = ${matrix.minAmount},
            max_amount = ${matrix.maxAmount},
            currency = ${matrix.currency},
            priority = ${matrix.priority},
            is_active = ${matrix.isActive},
            description = ${matrix.description},
            updated_at = now(),
            updated_by = ${organization.createdBy}
        where id = ${existing.id}
      `;
      continue;
    }

    await sql`
      insert into crm_approval_matrices (
        id,
        organization_id,
        entity_type,
        workflow_id,
        branch_id,
        product_type,
        min_amount,
        max_amount,
        currency,
        priority,
        is_default,
        is_active,
        description,
        created_by,
        updated_by
      ) values (
        ${crypto.randomUUID()},
        ${organization.id},
        ${matrix.entityType},
        ${workflowId},
        ${matrix.branchId},
        ${matrix.productType},
        ${matrix.minAmount},
        ${matrix.maxAmount},
        ${matrix.currency},
        ${matrix.priority},
        ${matrix.isDefault},
        ${matrix.isActive},
        ${matrix.description},
        ${organization.createdBy},
        ${organization.createdBy}
      )
    `;
  }
}

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
  `;

  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 (${"exclusion_data_"})
      and deleted_at is null
  `;
}

async function upsertDocumentLibraryForOrganization(
  sql: SqlClient,
  organization: { id: string; name: string; createdBy: string },
) {
  const storageProvider = await getSeedStorageProvider();

  for (const librarySeed of DOCUMENT_LIBRARY_SEEDS) {
    const [libraryRow] = await sql`
      insert into crm_document_libraries (
        id,
        organization_id,
        code,
        name,
        document_type,
        description,
        brand,
        language,
        product_type,
        status,
        is_active,
        created_by,
        updated_by
      ) values (
        ${crypto.randomUUID()},
        ${organization.id},
        ${librarySeed.code},
        ${librarySeed.name},
        ${librarySeed.documentType},
        ${librarySeed.description},
        ${librarySeed.brand},
        ${librarySeed.language},
        ${librarySeed.productType},
        ${"active"},
        ${true},
        ${organization.createdBy},
        ${organization.createdBy}
      )
      on conflict (organization_id, code) do update
      set
        name = excluded.name,
        document_type = excluded.document_type,
        description = excluded.description,
        brand = excluded.brand,
        language = excluded.language,
        product_type = excluded.product_type,
        status = excluded.status,
        is_active = excluded.is_active,
        updated_by = excluded.updated_by,
        deleted_at = null,
        updated_at = now()
      returning id
    `;

    const hasSeededActiveVersion = librarySeed.versions.some(
      (version) => version.status === "active",
    );

    if (hasSeededActiveVersion) {
      await sql`
        update crm_document_library_versions
        set
          is_active = ${false},
          updated_at = now()
        where library_id = ${libraryRow.id}
      `;
    }

    for (const versionSeed of librarySeed.versions) {
      const existingVersion =
        (
          await sql`
            select id
            from crm_document_library_versions
            where library_id = ${libraryRow.id}
              and version = ${versionSeed.version}
            limit 1
          `
        )[0] ?? null;

      const versionId = existingVersion?.id ?? crypto.randomUUID();
      const pdfBuffer = await createDocumentLibrarySeedPdf({
        organizationName: organization.name,
        libraryName: librarySeed.name,
        documentType: librarySeed.documentType,
        brand: librarySeed.brand,
        language: librarySeed.language,
        productType: librarySeed.productType,
        version: versionSeed.version,
        summary: versionSeed.summary,
      });

      const storageKey = buildOrganizationStorageKey(organization.id, [
        "document-library",
        libraryRow.id,
        versionId,
        versionSeed.fileName,
      ]);

      const storedObject = await storageProvider.putObject({
        key: storageKey,
        body: pdfBuffer,
        contentType: "application/pdf",
        fileName: versionSeed.fileName,
      });

      const lifecycleTimestamps = resolveLifecycleTimestamps(
        versionSeed.status,
      );
      const isActiveVersion = versionSeed.status === "active";

      await sql`
        insert into crm_document_library_versions (
          id,
          organization_id,
          library_id,
          version,
          file_name,
          storage_provider,
          storage_key,
          mime_type,
          file_size,
          checksum,
          page_count,
          status,
          is_active,
          published_at,
          published_by,
          archived_at,
          archived_by,
          deleted_at,
          created_by
        ) values (
          ${versionId},
          ${organization.id},
          ${libraryRow.id},
          ${versionSeed.version},
          ${versionSeed.fileName},
          ${storedObject.provider},
          ${storedObject.key},
          ${"application/pdf"},
          ${pdfBuffer.byteLength},
          ${sha256(pdfBuffer)},
          ${1},
          ${versionSeed.status},
          ${isActiveVersion},
          ${lifecycleTimestamps.publishedAt},
          ${lifecycleTimestamps.publishedAt ? organization.createdBy : null},
          ${lifecycleTimestamps.archivedAt},
          ${lifecycleTimestamps.archivedAt ? organization.createdBy : null},
          ${null},
          ${organization.createdBy}
        )
        on conflict (library_id, version) do update
        set
          file_name = excluded.file_name,
          storage_provider = excluded.storage_provider,
          storage_key = excluded.storage_key,
          mime_type = excluded.mime_type,
          file_size = excluded.file_size,
          checksum = excluded.checksum,
          page_count = excluded.page_count,
          status = excluded.status,
          is_active = excluded.is_active,
          published_at = excluded.published_at,
          published_by = excluded.published_by,
          archived_at = excluded.archived_at,
          archived_by = excluded.archived_by,
          deleted_at = excluded.deleted_at,
          updated_at = now()
      `;
    }
  }
}

async function upsertReportDefinitionsForOrganization(
  sql: SqlClient,
  organization: { id: string },
) {
  for (const definition of REPORT_DEFINITIONS) {
    await sql`
      insert into crm_report_definitions (
        id,
        organization_id,
        code,
        name,
        description,
        category,
        is_system,
        is_active
      ) values (
        ${crypto.randomUUID()},
        ${organization.id},
        ${definition.code},
        ${definition.name},
        ${definition.description},
        ${definition.category},
        ${true},
        ${true}
      )
      on conflict (organization_id, code) do update
      set
        name = excluded.name,
        description = excluded.description,
        category = excluded.category,
        is_system = excluded.is_system,
        is_active = excluded.is_active,
        updated_at = now()
    `;
  }
}

async function upsertNotificationTemplatesForOrganization(
  sql: SqlClient,
  organization: { id: string; createdBy: string },
) {
  const templates = [
    {
      eventType: "approval.requested",
      titleTemplate: "Approval Required: {{quotationCode}}",
      bodyTemplate:
        "{{actorName}} submitted {{quotationCode}} for approval in {{workflowName}}.",
      linkTemplate: "{{entityLink}}",
    },
    {
      eventType: "approval.step.approved",
      titleTemplate: "Approval Updated: {{quotationCode}}",
      bodyTemplate:
        "{{actorName}} approved {{quotationCode}}. Next step: {{currentStepRoleName}}.",
      linkTemplate: "{{entityLink}}",
    },
    {
      eventType: "approval.step.rejected",
      titleTemplate: "Approval Rejected: {{quotationCode}}",
      bodyTemplate:
        "{{actorName}} rejected {{quotationCode}}. Reason: {{remark}}",
      linkTemplate: "{{entityLink}}",
    },
    {
      eventType: "approval.completed",
      titleTemplate: "Approval Completed: {{quotationCode}}",
      bodyTemplate:
        "{{quotationCode}} has been fully approved in {{workflowName}}.",
      linkTemplate: "{{entityLink}}",
    },
    {
      eventType: "approval.cancelled",
      titleTemplate: "Approval Cancelled: {{quotationCode}}",
      bodyTemplate:
        "{{actorName}} cancelled the approval request for {{quotationCode}}.",
      linkTemplate: "{{entityLink}}",
    },
    {
      eventType: "approval.returned",
      titleTemplate: "Approval Returned: {{quotationCode}}",
      bodyTemplate:
        "{{actorName}} returned {{quotationCode}} for revision. Reason: {{remark}}",
      linkTemplate: "{{entityLink}}",
    },
    {
      eventType: "approval.reminder",
      titleTemplate: "Approval Reminder: {{workflowName}}",
      bodyTemplate:
        "{{quotationCode}} is still waiting on {{currentStepRoleName}}. Please review it.",
      linkTemplate: "{{entityLink}}",
    },
    {
      eventType: "approval.escalated",
      titleTemplate: "Approval Escalated: {{workflowName}}",
      bodyTemplate:
        "{{quotationCode}} exceeded its escalation threshold at {{currentStepRoleName}}.",
      linkTemplate: "{{entityLink}}",
    },
    {
      eventType: "approval.timeout",
      titleTemplate: "Approval Timeout: {{workflowName}}",
      bodyTemplate:
        "{{quotationCode}} reached timeout handling at {{currentStepRoleName}}. {{remark}}",
      linkTemplate: "{{entityLink}}",
    },
  ];

  for (const template of templates) {
    await sql`
      insert into app_notification_templates (
        id,
        organization_id,
        event_type,
        channel,
        title_template,
        body_template,
        link_template,
        is_active,
        metadata,
        deleted_at,
        created_by,
        updated_by
      ) values (
        ${crypto.randomUUID()},
        ${organization.id},
        ${template.eventType},
        ${"in_app"},
        ${template.titleTemplate},
        ${template.bodyTemplate},
        ${template.linkTemplate},
        ${true},
        ${JSON.stringify({ seededBy: "foundation.seed" })},
        ${null},
        ${organization.createdBy},
        ${organization.createdBy}
      )
      on conflict (organization_id, event_type, channel) do update
      set
        title_template = excluded.title_template,
        body_template = excluded.body_template,
        link_template = excluded.link_template,
        is_active = excluded.is_active,
        metadata = excluded.metadata,
        deleted_at = excluded.deleted_at,
        updated_by = excluded.updated_by,
        updated_at = now()
    `;
  }
}

async function main() {
  loadLocalEnv();

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

  try {
    const organizations = await sql`
      select id, name, slug, 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 upsertDocumentSequencesForOrganizationV2(
        sql,
        organization,
        branchRows,
      );
      await upsertApprovalWorkflowsForOrganization(sql, organization);
      await upsertApprovalMatricesForOrganization(sql, organization);
      await upsertDocumentTemplatesForOrganization(sql, organization);
      await upsertDocumentLibraryForOrganization(sql, organization);
      await upsertNotificationTemplatesForOrganization(sql, organization);
      await upsertReportDefinitionsForOrganization(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;
  });
}
