const fs = require('node:fs');
const path = require('node:path');
const { hash } = require('bcryptjs');
const postgres = require('postgres');

const TASK_H1_PASSWORD = 'TaskH1Pass!2026';
const FIXTURE_CODES = {
  customer: 'CUS-TASK-H1',
  approvedQuotation: 'QT-TASK-H1-APPROVED',
  draftQuotation: 'QT-TASK-H1-DRAFT'
};

const FIXTURE_USERS = {
  admin: 'task-h1-admin@local.test',
  pdfUser: 'task-h1-pdf-user@local.test',
  viewer: 'task-h1-viewer@local.test'
};

const PDF_PERMISSIONS = [
  'crm.quotation.read',
  'crm.quotation.document.preview',
  'crm.quotation.pdf.preview',
  'crm.quotation.pdf.download',
  'crm.quotation.pdf.generate_approved',
  'crm.document_artifact.read',
  'crm.document_artifact.download'
];

function loadEnvFile(filePath) {
  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) {
  const value = process.env[name]?.trim();

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

  return value;
}

async function resolveOptionId(sql, organizationId, category, code) {
  const [row] = await sql`
    select id
    from ms_options
    where organization_id = ${organizationId}
      and category = ${category}
      and code = ${code}
      and is_active = true
      and deleted_at is null
    limit 1
  `;

  if (!row) {
    throw new Error(`Missing option ${category}:${code}`);
  }

  return row.id;
}

async function resolveApprovalWorkflow(sql, organizationId) {
  const [workflow] = await sql`
    select id
    from crm_approval_workflows
    where organization_id = ${organizationId}
      and code = 'quotation_standard_approval'
      and is_active = true
      and deleted_at is null
    limit 1
  `;

  if (!workflow) {
    throw new Error('Missing quotation_standard_approval workflow');
  }

  const steps = await sql`
    select step_number as "stepNumber", role_code as "roleCode", role_name as "roleName"
    from crm_approval_steps
    where organization_id = ${organizationId}
      and workflow_id = ${workflow.id}
      and deleted_at is null
    order by step_number asc
  `;

  if (!steps.length) {
    throw new Error('Approval workflow has no steps');
  }

  return {
    id: workflow.id,
    steps
  };
}

async function ensureUser(sql, organization, creatorId, email, name, membership) {
  const passwordHash = await hash(TASK_H1_PASSWORD, 10);
  const existing =
    (
      await sql`
        select id
        from users
        where email = ${email}
        limit 1
      `
    )[0] ?? null;
  const userId = existing?.id ?? crypto.randomUUID();

  if (existing) {
    await sql`
      update users
      set
        name = ${name},
        password_hash = ${passwordHash},
        system_role = 'user',
        active_organization_id = ${organization.id},
        updated_at = now()
      where id = ${userId}
    `;
  } else {
    await sql`
      insert into users (
        id,
        name,
        email,
        password_hash,
        system_role,
        active_organization_id
      ) values (
        ${userId},
        ${name},
        ${email},
        ${passwordHash},
        'user',
        ${organization.id}
      )
    `;
  }

  const membershipRow =
    (
      await sql`
        select id
        from memberships
        where user_id = ${userId}
          and organization_id = ${organization.id}
        limit 1
      `
    )[0] ?? null;

  if (membershipRow) {
    await sql`
      update memberships
      set
        role = ${membership.role},
        business_role = ${membership.businessRole},
        permissions = ${membership.permissions},
        updated_at = now()
      where id = ${membershipRow.id}
    `;
  } else {
    await sql`
      insert into memberships (
        id,
        user_id,
        organization_id,
        role,
        business_role,
        permissions
      ) values (
        ${crypto.randomUUID()},
        ${userId},
        ${organization.id},
        ${membership.role},
        ${membership.businessRole},
        ${membership.permissions}
      )
    `;
  }

  return {
    id: userId,
    email
  };
}

async function ensureCustomerAndContact(sql, organization, creatorId, optionIds) {
  const existingCustomer =
    (
      await sql`
        select id
        from crm_customers
        where organization_id = ${organization.id}
          and code = ${FIXTURE_CODES.customer}
        limit 1
      `
    )[0] ?? null;
  const customerId = existingCustomer?.id ?? crypto.randomUUID();

  if (existingCustomer) {
    await sql`
      update crm_customers
      set
        branch_id = ${optionIds.branchId},
        name = 'Task H.1 Demo Customer',
        abbr = 'TASKH1',
        tax_id = '0105559001122',
        customer_type = ${optionIds.customerTypeCompanyId},
        customer_status = ${optionIds.customerStatusActiveId},
        address = '99/9 Rama IX Road, Bangkok 10310',
        phone = '02-000-1000',
        email = 'demo-customer@local.test',
        lead_channel = ${optionIds.leadChannelSalesId},
        customer_group = ${optionIds.customerGroupStandardId},
        notes = 'Task H.1 smoke-test customer',
        is_active = true,
        deleted_at = null,
        updated_at = now(),
        updated_by = ${creatorId}
      where id = ${customerId}
    `;
  } else {
    await sql`
      insert into crm_customers (
        id,
        organization_id,
        branch_id,
        code,
        name,
        abbr,
        tax_id,
        customer_type,
        customer_status,
        address,
        phone,
        email,
        lead_channel,
        customer_group,
        notes,
        is_active,
        created_by,
        updated_by
      ) values (
        ${customerId},
        ${organization.id},
        ${optionIds.branchId},
        ${FIXTURE_CODES.customer},
        'Task H.1 Demo Customer',
        'TASKH1',
        '0105559001122',
        ${optionIds.customerTypeCompanyId},
        ${optionIds.customerStatusActiveId},
        '99/9 Rama IX Road, Bangkok 10310',
        '02-000-1000',
        'demo-customer@local.test',
        ${optionIds.leadChannelSalesId},
        ${optionIds.customerGroupStandardId},
        'Task H.1 smoke-test customer',
        true,
        ${creatorId},
        ${creatorId}
      )
    `;
  }

  const existingContact =
    (
      await sql`
        select id
        from crm_customer_contacts
        where organization_id = ${organization.id}
          and customer_id = ${customerId}
          and email = 'demo-contact@local.test'
        limit 1
      `
    )[0] ?? null;
  const contactId = existingContact?.id ?? crypto.randomUUID();

  if (existingContact) {
    await sql`
      update crm_customer_contacts
      set
        name = 'Task H.1 Contact',
        position = 'Procurement Manager',
        department = 'Procurement',
        phone = '02-000-1001',
        mobile = '081-000-1001',
        email = 'demo-contact@local.test',
        is_primary = true,
        notes = 'Primary smoke-test contact',
        is_active = true,
        deleted_at = null,
        updated_at = now(),
        updated_by = ${creatorId}
      where id = ${contactId}
    `;
  } else {
    await sql`
      insert into crm_customer_contacts (
        id,
        organization_id,
        customer_id,
        name,
        position,
        department,
        phone,
        mobile,
        email,
        is_primary,
        notes,
        is_active,
        created_by,
        updated_by
      ) values (
        ${contactId},
        ${organization.id},
        ${customerId},
        'Task H.1 Contact',
        'Procurement Manager',
        'Procurement',
        '02-000-1001',
        '081-000-1001',
        'demo-contact@local.test',
        true,
        'Primary smoke-test contact',
        true,
        ${creatorId},
        ${creatorId}
      )
    `;
  }

  return {
    customerId,
    contactId
  };
}

async function upsertQuotation(sql, args) {
  const {
    organization,
    creatorId,
    workflow,
    optionIds,
    customerId,
    contactId,
    code,
    approved
  } = args;
  const existing =
    (
      await sql`
        select id
        from crm_quotations
        where organization_id = ${organization.id}
          and code = ${code}
        limit 1
      `
    )[0] ?? null;
  const quotationId = existing?.id ?? crypto.randomUUID();
  const statusId = approved ? optionIds.quotationStatusApprovedId : optionIds.quotationStatusDraftId;
  const approvalNow = approved ? new Date().toISOString() : null;
  const notes = approved
    ? 'Approved quotation fixture for Task H.1 PDF stabilization.'
    : 'Draft quotation fixture for Task H.1 negative API validation.';

  if (existing) {
    await sql`
      update crm_quotations
      set
        branch_id = ${optionIds.branchId},
        enquiry_id = null,
        customer_id = ${customerId},
        contact_id = ${contactId},
        quotation_date = ${'2026-06-16T00:00:00.000Z'},
        valid_until = ${'2026-07-16T00:00:00.000Z'},
        quotation_type = ${optionIds.quotationTypeServiceId},
        project_name = ${approved ? 'Task H.1 Approved PDF Fixture' : 'Task H.1 Draft PDF Fixture'},
        project_location = 'Bangkok HQ',
        attention = 'Task H.1 Contact',
        reference = ${approved ? 'TASK-H1-APPROVED' : 'TASK-H1-DRAFT'},
        notes = ${notes},
        status = ${statusId},
        revision = 0,
        parent_quotation_id = null,
        revision_remark = null,
        currency = ${optionIds.currencyThbId},
        exchange_rate = 1,
        subtotal = 125000,
        discount = 5000,
        discount_type = ${optionIds.discountPercentageId},
        tax_rate = 7,
        tax_amount = 8400,
        total_amount = 128400,
        chance_percent = 90,
        is_hot_project = true,
        competitor = 'Task H.1 Competitor',
        salesman_id = ${creatorId},
        is_sent = ${approved},
        sent_at = ${approved ? '2026-06-16T01:00:00.000Z' : null},
        sent_via = ${optionIds.sentViaEmailId},
        approved_at = ${approvalNow},
        approved_pdf_url = null,
        approved_snapshot = null,
        approved_template_version_id = null,
        accepted_at = null,
        rejected_at = null,
        rejection_reason = null,
        is_active = true,
        deleted_at = null,
        updated_at = now(),
        updated_by = ${creatorId}
      where id = ${quotationId}
    `;
  } else {
    await sql`
      insert into crm_quotations (
        id,
        organization_id,
        branch_id,
        code,
        enquiry_id,
        customer_id,
        contact_id,
        quotation_date,
        valid_until,
        quotation_type,
        project_name,
        project_location,
        attention,
        reference,
        notes,
        status,
        revision,
        parent_quotation_id,
        revision_remark,
        currency,
        exchange_rate,
        subtotal,
        discount,
        discount_type,
        tax_rate,
        tax_amount,
        total_amount,
        chance_percent,
        is_hot_project,
        competitor,
        salesman_id,
        is_sent,
        sent_at,
        sent_via,
        approved_at,
        approved_pdf_url,
        approved_snapshot,
        approved_template_version_id,
        accepted_at,
        rejected_at,
        rejection_reason,
        is_active,
        created_by,
        updated_by
      ) values (
        ${quotationId},
        ${organization.id},
        ${optionIds.branchId},
        ${code},
        null,
        ${customerId},
        ${contactId},
        ${'2026-06-16T00:00:00.000Z'},
        ${'2026-07-16T00:00:00.000Z'},
        ${optionIds.quotationTypeServiceId},
        ${approved ? 'Task H.1 Approved PDF Fixture' : 'Task H.1 Draft PDF Fixture'},
        'Bangkok HQ',
        'Task H.1 Contact',
        ${approved ? 'TASK-H1-APPROVED' : 'TASK-H1-DRAFT'},
        ${notes},
        ${statusId},
        0,
        null,
        null,
        ${optionIds.currencyThbId},
        1,
        125000,
        5000,
        ${optionIds.discountPercentageId},
        7,
        8400,
        128400,
        90,
        true,
        'Task H.1 Competitor',
        ${creatorId},
        ${approved},
        ${approved ? '2026-06-16T01:00:00.000Z' : null},
        ${optionIds.sentViaEmailId},
        ${approvalNow},
        null,
        null,
        null,
        null,
        null,
        null,
        true,
        ${creatorId},
        ${creatorId}
      )
    `;
  }

  await sql`delete from crm_quotation_attachments where quotation_id = ${quotationId}`;
  await sql`
    delete from crm_approval_actions
    where approval_request_id in (
      select id
      from crm_approval_requests
      where entity_type = 'quotation'
        and entity_id = ${quotationId}
    )
  `;
  await sql`
    delete from crm_approval_requests
    where entity_type = 'quotation'
      and entity_id = ${quotationId}
  `;
  await sql`delete from crm_quotation_customers where quotation_id = ${quotationId}`;
  await sql`
    delete from crm_quotation_topic_items
    where topic_id in (
      select id
      from crm_quotation_topics
      where quotation_id = ${quotationId}
    )
  `;
  await sql`delete from crm_quotation_topics where quotation_id = ${quotationId}`;
  await sql`delete from crm_quotation_items where quotation_id = ${quotationId}`;

  await sql`
    insert into crm_quotation_items (
      id,
      organization_id,
      quotation_id,
      item_number,
      product_type,
      description,
      quantity,
      unit,
      unit_price,
      discount,
      discount_type,
      tax_rate,
      total_price,
      notes,
      sort_order
    ) values (
      ${crypto.randomUUID()},
      ${organization.id},
      ${quotationId},
      1,
      ${optionIds.productTypeCraneId},
      '1 set overhead crane with installation and commissioning',
      1,
      ${optionIds.unitSetId},
      125000,
      5000,
      ${optionIds.discountPercentageId},
      7,
      120000,
      'Smoke-test commercial line',
      1
    )
  `;

  await sql`
    insert into crm_quotation_customers (
      id,
      organization_id,
      quotation_id,
      customer_id,
      role,
      is_primary
    ) values (
      ${crypto.randomUUID()},
      ${organization.id},
      ${quotationId},
      ${customerId},
      ${optionIds.customerRoleOwnerId},
      true
    )
  `;

  const topics = [
    {
      typeId: optionIds.topicTypeScopeId,
      title: 'Scope of work',
      items: ['Supply crane equipment', 'Install and test the system']
    },
    {
      typeId: optionIds.topicTypeExclusionId,
      title: 'Exclusions',
      items: ['Civil work by customer', 'Power feeder by customer']
    },
    {
      typeId: optionIds.topicTypePaymentId,
      title: 'Payment terms',
      items: ['50% upon PO', '40% before delivery', '10% after commissioning']
    }
  ];

  for (const [topicIndex, topic] of topics.entries()) {
    const topicId = crypto.randomUUID();

    await sql`
      insert into crm_quotation_topics (
        id,
        organization_id,
        quotation_id,
        topic_type,
        title,
        sort_order
      ) values (
        ${topicId},
        ${organization.id},
        ${quotationId},
        ${topic.typeId},
        ${topic.title},
        ${topicIndex + 1}
      )
    `;

    for (const [itemIndex, content] of topic.items.entries()) {
      await sql`
        insert into crm_quotation_topic_items (
          id,
          organization_id,
          topic_id,
          content,
          sort_order
        ) values (
          ${crypto.randomUUID()},
          ${organization.id},
          ${topicId},
          ${content},
          ${itemIndex + 1}
        )
      `;
    }
  }

  if (approved) {
    const approvalRequestId = crypto.randomUUID();

    await sql`
      insert into crm_approval_requests (
        id,
        organization_id,
        workflow_id,
        entity_type,
        entity_id,
        current_step,
        status,
        requested_by,
        requested_at,
        completed_at
      ) values (
        ${approvalRequestId},
        ${organization.id},
        ${workflow.id},
        'quotation',
        ${quotationId},
        ${workflow.steps[workflow.steps.length - 1].stepNumber},
        'approved',
        ${creatorId},
        ${'2026-06-16T00:30:00.000Z'},
        ${'2026-06-16T01:30:00.000Z'}
      )
    `;

    await sql`
      insert into crm_approval_actions (
        id,
        organization_id,
        approval_request_id,
        step_number,
        action,
        remark,
        acted_by,
        acted_at
      ) values (
        ${crypto.randomUUID()},
        ${organization.id},
        ${approvalRequestId},
        0,
        'submit',
        'Task H.1 fixture submission',
        ${creatorId},
        ${'2026-06-16T00:30:00.000Z'}
      )
    `;

    for (const [index, step] of workflow.steps.entries()) {
      await sql`
        insert into crm_approval_actions (
          id,
          organization_id,
          approval_request_id,
          step_number,
          action,
          remark,
          acted_by,
          acted_at
        ) values (
          ${crypto.randomUUID()},
          ${organization.id},
          ${approvalRequestId},
          ${step.stepNumber},
          'approve',
          ${`Task H.1 fixture ${step.roleCode} approval`},
          ${creatorId},
          ${new Date(Date.UTC(2026, 5, 16, 1, index * 10, 0)).toISOString()}
        )
      `;
    }
  }

  return quotationId;
}

async function main() {
  loadLocalEnv();

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

  try {
    const organization =
      (
        await sql`
          select id, name, slug, created_by as "createdBy"
          from organizations
          order by created_at asc
          limit 1
        `
      )[0] ?? null;

    if (!organization) {
      throw new Error('No organization found for Task H.1 fixture');
    }

    const creatorId =
      (
        await sql`
          select id
          from users
          where system_role = 'super_admin'
          order by created_at asc
          limit 1
        `
      )[0]?.id ?? organization.createdBy;

    if (!creatorId) {
      throw new Error('No creator user found for Task H.1 fixture');
    }

    const optionIds = {
      branchId: await resolveOptionId(sql, organization.id, 'crm_branch', 'head_office'),
      customerTypeCompanyId: await resolveOptionId(
        sql,
        organization.id,
        'crm_customer_type',
        'company'
      ),
      customerStatusActiveId: await resolveOptionId(
        sql,
        organization.id,
        'crm_customer_status',
        'active'
      ),
      leadChannelSalesId: await resolveOptionId(sql, organization.id, 'crm_lead_channel', 'sales'),
      customerGroupStandardId: await resolveOptionId(
        sql,
        organization.id,
        'crm_customer_group',
        'standard'
      ),
      quotationStatusDraftId: await resolveOptionId(
        sql,
        organization.id,
        'crm_quotation_status',
        'draft'
      ),
      quotationStatusApprovedId: await resolveOptionId(
        sql,
        organization.id,
        'crm_quotation_status',
        'approved'
      ),
      quotationTypeServiceId: await resolveOptionId(
        sql,
        organization.id,
        'crm_quotation_type',
        'service'
      ),
      currencyThbId: await resolveOptionId(sql, organization.id, 'crm_currency', 'THB'),
      discountPercentageId: await resolveOptionId(
        sql,
        organization.id,
        'crm_discount_type',
        'percentage'
      ),
      unitSetId: await resolveOptionId(sql, organization.id, 'crm_unit', 'set'),
      productTypeCraneId: await resolveOptionId(
        sql,
        organization.id,
        'crm_product_type',
        'crane'
      ),
      customerRoleOwnerId: await resolveOptionId(
        sql,
        organization.id,
        'crm_quotation_customer_role',
        'owner'
      ),
      topicTypeScopeId: await resolveOptionId(
        sql,
        organization.id,
        'crm_quotation_topic_type',
        'scope'
      ),
      topicTypeExclusionId: await resolveOptionId(
        sql,
        organization.id,
        'crm_quotation_topic_type',
        'exclusion'
      ),
      topicTypePaymentId: await resolveOptionId(
        sql,
        organization.id,
        'crm_quotation_topic_type',
        'payment'
      ),
      sentViaEmailId: await resolveOptionId(sql, organization.id, 'crm_sent_via', 'email')
    };
    const workflow = await resolveApprovalWorkflow(sql, organization.id);
    const { customerId, contactId } = await ensureCustomerAndContact(
      sql,
      organization,
      creatorId,
      optionIds
    );

    const users = {
      admin: await ensureUser(sql, organization, creatorId, FIXTURE_USERS.admin, 'Task H.1 Admin', {
        role: 'admin',
        businessRole: 'sales_manager',
        permissions: [...PDF_PERMISSIONS, 'users:manage']
      }),
      pdfUser: await ensureUser(
        sql,
        organization,
        creatorId,
        FIXTURE_USERS.pdfUser,
        'Task H.1 PDF User',
        {
          role: 'user',
          businessRole: 'sales_support',
          permissions: PDF_PERMISSIONS
        }
      ),
      viewer: await ensureUser(sql, organization, creatorId, FIXTURE_USERS.viewer, 'Task H.1 Viewer', {
        role: 'user',
        businessRole: 'sales_support',
        permissions: ['crm.quotation.read']
      })
    };

    const approvedQuotationId = await upsertQuotation(sql, {
      organization,
      creatorId,
      workflow,
      optionIds,
      customerId,
      contactId,
      code: FIXTURE_CODES.approvedQuotation,
      approved: true
    });
    const draftQuotationId = await upsertQuotation(sql, {
      organization,
      creatorId,
      workflow,
      optionIds,
      customerId,
      contactId,
      code: FIXTURE_CODES.draftQuotation,
      approved: false
    });

    console.log(
      JSON.stringify(
        {
          organizationId: organization.id,
          approvedQuotationId,
          approvedQuotationCode: FIXTURE_CODES.approvedQuotation,
          draftQuotationId,
          draftQuotationCode: FIXTURE_CODES.draftQuotation,
          users,
          password: TASK_H1_PASSWORD
        },
        null,
        2
      )
    );
  } finally {
    await sql.end();
  }
}

main().catch((error) => {
  console.error('[seed-task-h1-fixture] Failed:', error.message);
  process.exitCode = 1;
});
