import { plainToInstance } from 'class-transformer';
import { validate } from 'class-validator';
import { describe, it, expect } from 'vitest';
import {
  GenerateKycUploadUrlsDto,
  KycFileRequestDto,
} from '../dto/generate-kyc-upload-urls.dto';

const validFile = (): KycFileRequestDto => {
  const file = new KycFileRequestDto();
  file.field = 'frontImage';
  file.mimeType = 'image/jpeg';
  file.fileName = 'cccd-front.jpg';
  return file;
};

describe('KycFileRequestDto', () => {
  it('accepts valid payload', async () => {
    const errors = await validate(validFile());
    expect(errors).toHaveLength(0);
  });

  it.each(['frontImage', 'backImage', 'selfieImage'] as const)(
    'accepts allowed field value: %s',
    async (value) => {
      const file = validFile();
      file.field = value;
      const errors = await validate(file);
      expect(errors).toHaveLength(0);
    },
  );

  it('rejects unknown field enum value', async () => {
    const file = validFile();
    (file as unknown as { field: string }).field = 'backgroundImage';
    const errors = await validate(file);
    expect(errors.some((e) => e.property === 'field')).toBe(true);
  });

  it('rejects empty mimeType', async () => {
    const file = validFile();
    file.mimeType = '';
    const errors = await validate(file);
    expect(errors.some((e) => e.property === 'mimeType')).toBe(true);
  });

  it('rejects non-string mimeType', async () => {
    const file = validFile();
    (file as unknown as { mimeType: unknown }).mimeType = 123;
    const errors = await validate(file);
    expect(errors.some((e) => e.property === 'mimeType')).toBe(true);
  });

  it('rejects empty fileName', async () => {
    const file = validFile();
    file.fileName = '';
    const errors = await validate(file);
    expect(errors.some((e) => e.property === 'fileName')).toBe(true);
  });

  it('rejects non-string fileName', async () => {
    const file = validFile();
    (file as unknown as { fileName: unknown }).fileName = null;
    const errors = await validate(file);
    expect(errors.some((e) => e.property === 'fileName')).toBe(true);
  });
});

describe('GenerateKycUploadUrlsDto', () => {
  const validPayload = (fileCount: 1 | 2 | 3 = 1): unknown => {
    const fields = ['frontImage', 'backImage', 'selfieImage'] as const;
    return {
      files: Array.from({ length: fileCount }, (_, i) => ({
        field: fields[i],
        mimeType: 'image/jpeg',
        fileName: `kyc-${fields[i]}.jpg`,
      })),
    };
  };

  it('accepts single-file payload', async () => {
    const dto = plainToInstance(GenerateKycUploadUrlsDto, validPayload(1));
    const errors = await validate(dto);
    expect(errors).toHaveLength(0);
  });

  it('accepts three-file payload', async () => {
    const dto = plainToInstance(GenerateKycUploadUrlsDto, validPayload(3));
    const errors = await validate(dto);
    expect(errors).toHaveLength(0);
  });

  it('rejects missing files array', async () => {
    const dto = plainToInstance(GenerateKycUploadUrlsDto, {});
    const errors = await validate(dto);
    expect(errors.some((e) => e.property === 'files')).toBe(true);
  });

  it('rejects empty files array (below ArrayMinSize)', async () => {
    const dto = plainToInstance(GenerateKycUploadUrlsDto, { files: [] });
    const errors = await validate(dto);
    expect(errors.some((e) => e.property === 'files')).toBe(true);
  });

  it('rejects more than three files (above ArrayMaxSize)', async () => {
    const payload = validPayload(3) as { files: unknown[] };
    payload.files.push({
      field: 'frontImage',
      mimeType: 'image/jpeg',
      fileName: 'extra.jpg',
    });
    const dto = plainToInstance(GenerateKycUploadUrlsDto, payload);
    const errors = await validate(dto);
    expect(errors.some((e) => e.property === 'files')).toBe(true);
  });

  it('rejects non-array files value', async () => {
    const dto = plainToInstance(GenerateKycUploadUrlsDto, {
      files: 'not-an-array',
    });
    const errors = await validate(dto);
    expect(errors.some((e) => e.property === 'files')).toBe(true);
  });

  it('validates nested KycFileRequestDto entries', async () => {
    const dto = plainToInstance(GenerateKycUploadUrlsDto, {
      files: [
        {
          field: 'invalidField',
          mimeType: '',
          fileName: '',
        },
      ],
    });
    const errors = await validate(dto);
    const filesError = errors.find((e) => e.property === 'files');
    expect(filesError).toBeDefined();
    expect(filesError?.children?.length ?? 0).toBeGreaterThan(0);
  });
});
