import { describe, expect, it } from 'vitest';
import {
  computeDaysUntilDate,
  computeEvergreenDecisionDueDate,
  computeEvergreenCycleBounds,
  computeNextEvergreenReviewAnchorDate,
  dedupeClientContractsByRenewalCycle,
  normalizeClientContract,
} from '../../../shared/billingClients/clientContracts';

describe('client contract effective renewal settings normalization', () => {
  it('applies tenant defaults when use_tenant_renewal_defaults is true', () => {
    const normalized = normalizeClientContract({
      contract_id: 'contract-1',
      client_contract_id: 'cc-1',
      client_id: 'client-1',
      tenant: 'tenant-1',
      start_date: '2026-01-01',
      end_date: '2026-12-31',
      is_active: true,
      use_tenant_renewal_defaults: true,
      renewal_mode: 'auto',
      notice_period_days: 10,
      tenant_default_renewal_mode: 'manual',
      tenant_default_notice_period_days: 45,
    });

    expect(normalized.use_tenant_renewal_defaults).toBe(true);
    expect(normalized.effective_renewal_mode).toBe('manual');
    expect(normalized.effective_notice_period_days).toBe(45);
    expect(normalized.assignment_status).toBe('active');
  });

  it('uses explicit values when tenant defaults are disabled', () => {
    const normalized = normalizeClientContract({
      contract_id: 'contract-2',
      client_contract_id: 'cc-2',
      client_id: 'client-2',
      tenant: 'tenant-1',
      start_date: '2026-01-01',
      end_date: '2026-12-31',
      is_active: true,
      use_tenant_renewal_defaults: false,
      renewal_mode: 'auto',
      notice_period_days: 60,
      tenant_default_renewal_mode: 'manual',
      tenant_default_notice_period_days: 30,
    });

    expect(normalized.use_tenant_renewal_defaults).toBe(false);
    expect(normalized.effective_renewal_mode).toBe('auto');
    expect(normalized.effective_notice_period_days).toBe(60);
  });

  it('falls back deterministically when explicit override values are partially missing', () => {
    const normalized = normalizeClientContract({
      contract_id: 'contract-3',
      client_contract_id: 'cc-3',
      client_id: 'client-3',
      tenant: 'tenant-1',
      start_date: '2026-01-01',
      end_date: null,
      is_active: true,
      use_tenant_renewal_defaults: false,
      renewal_mode: null,
      notice_period_days: undefined,
      tenant_default_renewal_mode: 'manual',
      tenant_default_notice_period_days: 20,
    });

    expect(normalized.effective_renewal_mode).toBe('manual');
    expect(normalized.effective_notice_period_days).toBe(20);
  });

  it('computes fixed-term decision_due_date from end_date minus effective notice period', () => {
    const normalized = normalizeClientContract({
      contract_id: 'contract-4',
      client_contract_id: 'cc-4',
      client_id: 'client-4',
      tenant: 'tenant-1',
      start_date: '2026-01-01',
      end_date: '2026-12-31',
      is_active: true,
      use_tenant_renewal_defaults: false,
      renewal_mode: 'manual',
      notice_period_days: 45,
    });

    expect(normalized.effective_notice_period_days).toBe(45);
    expect(normalized.decision_due_date).toBe('2026-11-16');
    expect(typeof normalized.days_until_due).toBe('number');
  });

  it('normalizes timestamp end_date to date-only semantics before fixed-term due-date math', () => {
    const normalized = normalizeClientContract({
      contract_id: 'contract-4b',
      client_contract_id: 'cc-4b',
      client_id: 'client-4b',
      tenant: 'tenant-1',
      start_date: '2026-01-01',
      end_date: '2026-12-31T23:59:59.999Z',
      is_active: true,
      use_tenant_renewal_defaults: false,
      renewal_mode: 'manual',
      notice_period_days: 1,
    });

    expect(normalized.decision_due_date).toBe('2026-12-30');
  });

  it('recomputes fixed-term decision_due_date when end_date changes', () => {
    const baseAssignment = {
      contract_id: 'contract-4c',
      client_contract_id: 'cc-4c',
      client_id: 'client-4c',
      tenant: 'tenant-1',
      start_date: '2026-01-01',
      is_active: true,
      use_tenant_renewal_defaults: false,
      renewal_mode: 'manual',
      notice_period_days: 30,
    };

    const before = normalizeClientContract({
      ...baseAssignment,
      end_date: '2026-12-31',
    });
    const after = normalizeClientContract({
      ...baseAssignment,
      end_date: '2027-01-31',
    });

    expect(before.decision_due_date).toBe('2026-12-01');
    expect(after.decision_due_date).toBe('2027-01-01');
  });

  it('recomputes fixed-term decision_due_date when notice period changes', () => {
    const baseAssignment = {
      contract_id: 'contract-4d',
      client_contract_id: 'cc-4d',
      client_id: 'client-4d',
      tenant: 'tenant-1',
      start_date: '2026-01-01',
      end_date: '2026-12-31',
      is_active: true,
      use_tenant_renewal_defaults: false,
      renewal_mode: 'manual',
    };

    const shortNotice = normalizeClientContract({
      ...baseAssignment,
      notice_period_days: 15,
    });
    const longNotice = normalizeClientContract({
      ...baseAssignment,
      notice_period_days: 45,
    });

    expect(shortNotice.decision_due_date).toBe('2026-12-16');
    expect(longNotice.decision_due_date).toBe('2026-11-16');
  });

  it('recomputes decision_due_date when renewal mode changes between none/manual/auto', () => {
    const baseAssignment = {
      contract_id: 'contract-4e',
      client_contract_id: 'cc-4e',
      client_id: 'client-4e',
      tenant: 'tenant-1',
      start_date: '2026-01-01',
      end_date: '2026-12-31',
      is_active: true,
      use_tenant_renewal_defaults: false,
      notice_period_days: 30,
    };

    const noneMode = normalizeClientContract({
      ...baseAssignment,
      renewal_mode: 'none',
    });
    const manualMode = normalizeClientContract({
      ...baseAssignment,
      renewal_mode: 'manual',
    });
    const autoMode = normalizeClientContract({
      ...baseAssignment,
      renewal_mode: 'auto',
    });

    expect(noneMode.decision_due_date).toBeUndefined();
    expect(manualMode.decision_due_date).toBe('2026-12-01');
    expect(autoMode.decision_due_date).toBe('2026-12-01');
  });

  it('skips decision_due_date generation only when the assignment itself is inactive', () => {
    const inactiveAssignment = normalizeClientContract({
      contract_id: 'contract-4f',
      client_contract_id: 'cc-4f',
      client_id: 'client-4f',
      tenant: 'tenant-1',
      start_date: '2026-01-01',
      end_date: '2026-12-31',
      is_active: false,
      use_tenant_renewal_defaults: false,
      renewal_mode: 'manual',
      notice_period_days: 30,
    });
    const terminatedContract = normalizeClientContract({
      contract_id: 'contract-4g',
      client_contract_id: 'cc-4g',
      client_id: 'client-4g',
      tenant: 'tenant-1',
      start_date: '2026-01-01',
      end_date: '2026-12-31',
      is_active: true,
      contract_status: 'terminated',
      use_tenant_renewal_defaults: false,
      renewal_mode: 'manual',
      notice_period_days: 30,
    });

    expect(inactiveAssignment.decision_due_date).toBeUndefined();
    expect(inactiveAssignment.evergreen_review_anchor_date).toBeUndefined();
    expect(terminatedContract.decision_due_date).toBe('2026-12-01');
    expect(terminatedContract.evergreen_review_anchor_date).toBeUndefined();
  });

  it('creates one renewal_cycle_key per computed contract cycle for deduplication', () => {
    const fixedTerm = normalizeClientContract({
      contract_id: 'contract-4h',
      client_contract_id: 'cc-4h',
      client_id: 'client-4h',
      tenant: 'tenant-1',
      start_date: '2026-01-01',
      end_date: '2026-12-31',
      is_active: true,
      use_tenant_renewal_defaults: false,
      renewal_mode: 'manual',
      notice_period_days: 30,
    });
    const evergreen = normalizeClientContract({
      contract_id: 'contract-4i',
      client_contract_id: 'cc-4i',
      client_id: 'client-4i',
      tenant: 'tenant-1',
      start_date: '2024-10-04',
      end_date: null,
      is_active: true,
      use_tenant_renewal_defaults: true,
      tenant_default_renewal_mode: 'manual',
      tenant_default_notice_period_days: 30,
    });
    const noneMode = normalizeClientContract({
      contract_id: 'contract-4j',
      client_contract_id: 'cc-4j',
      client_id: 'client-4j',
      tenant: 'tenant-1',
      start_date: '2026-01-01',
      end_date: '2026-12-31',
      is_active: true,
      use_tenant_renewal_defaults: false,
      renewal_mode: 'none',
      notice_period_days: 30,
    });

    expect(fixedTerm.renewal_cycle_key).toBe('fixed-term:2026-12-31');
    expect(fixedTerm.renewal_cycle_start).toBe('2026-01-01');
    expect(fixedTerm.renewal_cycle_end).toBe('2026-12-31');
    expect(evergreen.renewal_cycle_key).toMatch(/^evergreen:\d{4}-\d{2}-\d{2}$/);
    expect(evergreen.renewal_cycle_start).toMatch(/^\d{4}-\d{2}-\d{2}$/);
    expect(evergreen.renewal_cycle_end).toMatch(/^\d{4}-\d{2}-\d{2}$/);
    expect(noneMode.renewal_cycle_key).toBeUndefined();
  });

  it('deduplicates active rows by tenant + client_contract_id + renewal_cycle_key', () => {
    const duplicateRows = dedupeClientContractsByRenewalCycle([
      {
        tenant: 'tenant-1',
        client_contract_id: 'cc-dup',
        client_id: 'client-1',
        contract_id: 'contract-1',
        start_date: '2026-01-01',
        end_date: '2026-12-31',
        is_active: true,
        renewal_cycle_key: 'fixed-term:2026-12-31',
      },
      {
        tenant: 'tenant-1',
        client_contract_id: 'cc-dup',
        client_id: 'client-1',
        contract_id: 'contract-1',
        start_date: '2026-01-01',
        end_date: '2026-12-31',
        is_active: true,
        renewal_cycle_key: 'fixed-term:2026-12-31',
      },
      {
        tenant: 'tenant-1',
        client_contract_id: 'cc-dup',
        client_id: 'client-1',
        contract_id: 'contract-1',
        start_date: '2026-01-01',
        end_date: '2027-12-31',
        is_active: true,
        renewal_cycle_key: 'fixed-term:2027-12-31',
      },
    ] as any);

    expect(duplicateRows).toHaveLength(2);
    expect(duplicateRows.map((row: any) => row.renewal_cycle_key)).toEqual([
      'fixed-term:2026-12-31',
      'fixed-term:2027-12-31',
    ]);
  });

  it('computes next evergreen review anchor date using contract anniversary rules', () => {
    expect(
      computeNextEvergreenReviewAnchorDate({
        startDate: '2024-05-10',
        now: '2026-05-01',
      })
    ).toBe('2026-05-10');

    expect(
      computeNextEvergreenReviewAnchorDate({
        startDate: '2024-05-10',
        now: '2026-05-11',
      })
    ).toBe('2027-05-10');
  });

  it('computes evergreen decision_due_date from annual anchor minus notice period', () => {
    expect(
      computeEvergreenDecisionDueDate({
        startDate: '2024-05-10',
        now: '2026-05-01',
        noticePeriodDays: 20,
      })
    ).toBe('2026-04-20');
  });

  it('recomputes evergreen decision_due_date when anniversary anchor basis changes', () => {
    const now = '2026-01-01';
    const noticePeriodDays = 20;

    const marchAnniversary = computeEvergreenDecisionDueDate({
      startDate: '2024-03-10',
      now,
      noticePeriodDays,
    });
    const septemberAnniversary = computeEvergreenDecisionDueDate({
      startDate: '2024-09-10',
      now,
      noticePeriodDays,
    });

    expect(marchAnniversary).toBe('2026-02-18');
    expect(septemberAnniversary).toBe('2026-08-21');
  });

  it('respects evergreen-specific notice period overrides when tenant defaults are disabled', () => {
    const now = '2026-01-01';
    const startDate = '2024-09-10';

    const withTenantDefaults = normalizeClientContract({
      contract_id: 'contract-4k',
      client_contract_id: 'cc-4k',
      client_id: 'client-4k',
      tenant: 'tenant-1',
      start_date: startDate,
      end_date: null,
      is_active: true,
      use_tenant_renewal_defaults: true,
      tenant_default_renewal_mode: 'manual',
      tenant_default_notice_period_days: 30,
      notice_period_days: 10,
    } as any);

    const withContractOverride = normalizeClientContract({
      contract_id: 'contract-4l',
      client_contract_id: 'cc-4l',
      client_id: 'client-4l',
      tenant: 'tenant-1',
      start_date: startDate,
      end_date: null,
      is_active: true,
      use_tenant_renewal_defaults: false,
      renewal_mode: 'manual',
      tenant_default_renewal_mode: 'manual',
      tenant_default_notice_period_days: 30,
      notice_period_days: 10,
    } as any);

    const expectedTenantDueDate = computeEvergreenDecisionDueDate({
      startDate,
      now,
      noticePeriodDays: 30,
    });
    const expectedOverrideDueDate = computeEvergreenDecisionDueDate({
      startDate,
      now,
      noticePeriodDays: 10,
    });

    expect(expectedTenantDueDate).toBe('2026-08-11');
    expect(expectedOverrideDueDate).toBe('2026-08-31');
    expect(withTenantDefaults.decision_due_date).toBe(expectedTenantDueDate);
    expect(withContractOverride.decision_due_date).toBe(expectedOverrideDueDate);
    expect(withContractOverride.decision_due_date).not.toBe(withTenantDefaults.decision_due_date);
  });

  it('computes evergreen cycle_start and cycle_end boundaries per annual cycle', () => {
    expect(
      computeEvergreenCycleBounds({
        startDate: '2024-05-10',
        now: '2026-05-01',
      })
    ).toEqual({
      cycleStart: '2025-05-10',
      cycleEnd: '2026-05-10',
    });
  });

  it('computes days_until_due as a date-only integer day delta', () => {
    expect(
      computeDaysUntilDate({
        targetDate: '2026-05-10',
        now: '2026-05-01',
      })
    ).toBe(9);

    expect(
      computeDaysUntilDate({
        targetDate: '2026-05-01',
        now: '2026-05-10',
      })
    ).toBe(-9);

    expect(
      computeDaysUntilDate({
        targetDate: '1900-01-01',
        now: '2026-05-10',
      })
    ).toBe(-36500);

    expect(
      computeDaysUntilDate({
        targetDate: '2200-01-01',
        now: '2026-05-10',
      })
    ).toBe(36500);
  });

  it('exposes evergreen_review_anchor_date on active evergreen assignments', () => {
    const normalized = normalizeClientContract({
      contract_id: 'contract-5',
      client_contract_id: 'cc-5',
      client_id: 'client-5',
      tenant: 'tenant-1',
      start_date: '2024-10-04',
      end_date: null,
      is_active: true,
      use_tenant_renewal_defaults: true,
      tenant_default_renewal_mode: 'manual',
      tenant_default_notice_period_days: 30,
    });

    expect(normalized.evergreen_review_anchor_date).toMatch(/^\d{4}-\d{2}-\d{2}$/);
    expect(normalized.decision_due_date).toMatch(/^\d{4}-\d{2}-\d{2}$/);
  });
});
