import { Op } from 'sequelize';
import { getArchiveDateField, buildArchiveQueryPlan } from '../../src/libs/archive/policy';
import { TableRetentionPolicy } from '../../src/libs/archive/config';

describe('archive/policy', () => {
  describe('getArchiveDateField', () => {
    it('should return updated_at when model has updated_at attribute', () => {
      const mockModel = {
        rawAttributes: {
          id: {},
          updated_at: {},
          created_at: {},
        },
      } as any;

      expect(getArchiveDateField(mockModel)).toBe('updated_at');
    });

    it('should return created_at when model only has created_at attribute', () => {
      const mockModel = {
        rawAttributes: {
          id: {},
          created_at: {},
        },
      } as any;

      expect(getArchiveDateField(mockModel)).toBe('created_at');
    });

    it('should fallback to created_at when neither field exists', () => {
      const mockModel = {
        rawAttributes: {
          id: {},
          name: {},
        },
      } as any;

      expect(getArchiveDateField(mockModel)).toBe('created_at');
    });
  });

  describe('buildArchiveQueryPlan', () => {
    const cutoffDate = new Date('2024-01-01T00:00:00Z');

    it('should build basic query plan with date cutoff', () => {
      const mockModel = {
        rawAttributes: {
          id: {},
          created_at: {},
        },
      } as any;

      const policy: TableRetentionPolicy = {
        enabled: true,
        retentionDays: 90,
      };

      const { where, dateField } = buildArchiveQueryPlan(mockModel, policy, cutoffDate);

      expect(dateField).toBe('created_at');
      expect(where.created_at).toBeDefined();
      expect((where.created_at as any)[Op.lte]).toEqual(cutoffDate);
    });

    it('should use updated_at when available', () => {
      const mockModel = {
        rawAttributes: {
          id: {},
          updated_at: {},
          created_at: {},
        },
      } as any;

      const policy: TableRetentionPolicy = {
        enabled: true,
        retentionDays: 90,
      };

      const { where, dateField } = buildArchiveQueryPlan(mockModel, policy, cutoffDate);

      expect(dateField).toBe('updated_at');
      expect(where.updated_at).toBeDefined();
    });

    it('should include archivableStatuses condition when model has status field', () => {
      const mockModel = {
        rawAttributes: {
          id: {},
          status: {},
          created_at: {},
        },
      } as any;

      const policy: TableRetentionPolicy = {
        enabled: true,
        retentionDays: 90,
        archivableStatuses: ['completed', 'canceled'],
      };

      const { where } = buildArchiveQueryPlan(mockModel, policy, cutoffDate);
      const conditions = (where as any)[Op.and];

      expect(conditions).toBeDefined();
      expect(conditions).toContainEqual({
        status: { [Op.in]: ['completed', 'canceled'] },
      });
    });

    it('should include excludeConditions.statuses when model has status field', () => {
      const mockModel = {
        rawAttributes: {
          id: {},
          status: {},
          created_at: {},
        },
      } as any;

      const policy: TableRetentionPolicy = {
        enabled: true,
        retentionDays: 90,
        excludeConditions: {
          statuses: ['pending', 'processing'],
        },
      };

      const { where } = buildArchiveQueryPlan(mockModel, policy, cutoffDate);
      const conditions = (where as any)[Op.and];

      expect(conditions).toBeDefined();
      expect(conditions).toContainEqual({
        status: { [Op.notIn]: ['pending', 'processing'] },
      });
    });

    it('should not add status conditions when model lacks status field', () => {
      const mockModel = {
        rawAttributes: {
          id: {},
          created_at: {},
        },
      } as any;

      const policy: TableRetentionPolicy = {
        enabled: true,
        retentionDays: 90,
        archivableStatuses: ['completed'],
        excludeConditions: {
          statuses: ['pending'],
        },
      };

      const { where } = buildArchiveQueryPlan(mockModel, policy, cutoffDate);
      const conditions = (where as any)[Op.and];

      expect(conditions).toBeUndefined();
    });

    it('should include custom condition wrapped with NOT()', () => {
      const mockModel = {
        rawAttributes: {
          id: {},
          created_at: {},
        },
      } as any;

      const policy: TableRetentionPolicy = {
        enabled: true,
        retentionDays: 90,
        excludeConditions: {
          customCondition: 'pending_webhooks > 0',
        },
      };

      const { where } = buildArchiveQueryPlan(mockModel, policy, cutoffDate);
      const conditions = (where as any)[Op.and];

      expect(conditions).toBeDefined();
      expect(conditions.length).toBe(1);
      // Verify the Sequelize.literal wraps with NOT()
      const literal = conditions[0];
      expect(literal.val).toContain('NOT');
      expect(literal.val).toContain('pending_webhooks > 0');
    });

    it('should combine multiple conditions with AND', () => {
      const mockModel = {
        rawAttributes: {
          id: {},
          status: {},
          created_at: {},
        },
      } as any;

      const policy: TableRetentionPolicy = {
        enabled: true,
        retentionDays: 90,
        archivableStatuses: ['completed'],
        excludeConditions: {
          statuses: ['pending'],
          customCondition: 'active = 0',
        },
      };

      const { where } = buildArchiveQueryPlan(mockModel, policy, cutoffDate);
      const conditions = (where as any)[Op.and];

      expect(conditions).toBeDefined();
      expect(conditions.length).toBe(3);
    });

    it('should handle empty archivableStatuses array', () => {
      const mockModel = {
        rawAttributes: {
          id: {},
          status: {},
          created_at: {},
        },
      } as any;

      const policy: TableRetentionPolicy = {
        enabled: true,
        retentionDays: 90,
        archivableStatuses: [],
      };

      const { where } = buildArchiveQueryPlan(mockModel, policy, cutoffDate);
      const conditions = (where as any)[Op.and];

      expect(conditions).toBeUndefined();
    });

    it('should handle empty excludeConditions.statuses array', () => {
      const mockModel = {
        rawAttributes: {
          id: {},
          status: {},
          created_at: {},
        },
      } as any;

      const policy: TableRetentionPolicy = {
        enabled: true,
        retentionDays: 90,
        excludeConditions: {
          statuses: [],
        },
      };

      const { where } = buildArchiveQueryPlan(mockModel, policy, cutoffDate);
      const conditions = (where as any)[Op.and];

      expect(conditions).toBeUndefined();
    });
  });
});
