import { describe, expect, it } from 'vitest';
import {
  extractVariableIndices,
  TEMPLATE_LIMITS,
  validateBody,
  validateButtons,
  validateFooter,
  validateHeader,
  validateSampleValues,
  validateTemplateName,
  validateTemplatePayload,
  type TemplatePayload,
} from './template-validators';

const baseValid: TemplatePayload = {
  name: 'order_confirmation',
  category: 'Utility',
  language: 'en_US',
  body_text: 'Your order is confirmed.',
};

describe('extractVariableIndices', () => {
  it('returns sorted unique 1-based indices', () => {
    expect(extractVariableIndices('Hi {{2}} and {{1}} {{2}}')).toEqual([1, 2]);
  });
  it('returns empty array for no variables', () => {
    expect(extractVariableIndices('No vars here')).toEqual([]);
  });
});

describe('validateTemplateName', () => {
  it('accepts lowercase + digits + underscore', () => {
    expect(() => validateTemplateName('order_v2')).not.toThrow();
  });
  it('rejects uppercase', () => {
    expect(() => validateTemplateName('OrderV2')).toThrow(/lowercase/);
  });
  it('rejects empty', () => {
    expect(() => validateTemplateName('')).toThrow(/required/);
  });
  it('rejects spaces and dashes', () => {
    expect(() => validateTemplateName('order v2')).toThrow();
    expect(() => validateTemplateName('order-v2')).toThrow();
  });
});

describe('validateBody', () => {
  it('rejects empty', () => {
    expect(() => validateBody('   ')).toThrow(/required/);
  });
  it('rejects > 1024 chars', () => {
    expect(() => validateBody('x'.repeat(TEMPLATE_LIMITS.bodyMaxLength + 1))).toThrow(
      /exceeds 1024/,
    );
  });
  it('rejects non-contiguous variables', () => {
    expect(() => validateBody('Hi {{1}} {{3}}')).toThrow(/contiguous/);
  });
  it('accepts contiguous variables', () => {
    expect(validateBody('Hi {{1}} {{2}}')).toEqual([1, 2]);
  });
});

describe('validateFooter', () => {
  it('accepts undefined', () => {
    expect(() => validateFooter(undefined)).not.toThrow();
  });
  it('rejects > 60 chars', () => {
    expect(() => validateFooter('x'.repeat(61))).toThrow(/60 chars/);
  });
  it('rejects variables in footer', () => {
    expect(() => validateFooter('Powered by {{1}}')).toThrow(/cannot contain/);
  });
});

describe('validateHeader', () => {
  it('text header requires content', () => {
    expect(() =>
      validateHeader({ header_type: 'text', header_content: '' }),
    ).toThrow(/requires header_content/);
  });
  it('text header rejects > 60 chars', () => {
    expect(() =>
      validateHeader({ header_type: 'text', header_content: 'x'.repeat(61) }),
    ).toThrow(/60 chars/);
  });
  it('text header rejects more than one variable', () => {
    expect(() =>
      validateHeader({ header_type: 'text', header_content: '{{1}} {{2}}' }),
    ).toThrow(/at most one variable/);
  });
  it('text header requires variable to be {{1}}', () => {
    expect(() =>
      validateHeader({ header_type: 'text', header_content: 'Hello {{2}}' }),
    ).toThrow(/must be \{\{1\}\}/);
  });
  it('image header requires a URL or handle', () => {
    expect(() => validateHeader({ header_type: 'image' })).toThrow(
      /requires either/,
    );
  });
  it('image header accepts a URL', () => {
    expect(() =>
      validateHeader({
        header_type: 'image',
        header_media_url: 'https://example.com/img.jpg',
      }),
    ).not.toThrow();
  });
  it('image header rejects a non-URL string', () => {
    expect(() =>
      validateHeader({
        header_type: 'image',
        header_media_url: 'not a url',
      }),
    ).toThrow(/valid URL/);
  });
});

describe('validateButtons', () => {
  it('accepts undefined / empty', () => {
    expect(() => validateButtons(undefined)).not.toThrow();
    expect(() => validateButtons([])).not.toThrow();
  });
  it('rejects > 10 buttons', () => {
    const tooMany = Array.from({ length: 11 }, () => ({
      type: 'QUICK_REPLY' as const,
      text: 'Hi',
    }));
    expect(() => validateButtons(tooMany)).toThrow(/at most 10 buttons/);
  });
  it('rejects > 2 URL buttons', () => {
    expect(() =>
      validateButtons([
        { type: 'URL', text: 'a', url: 'https://x' },
        { type: 'URL', text: 'b', url: 'https://x' },
        { type: 'URL', text: 'c', url: 'https://x' },
      ]),
    ).toThrow(/At most 2 URL/);
  });
  it('rejects > 1 PHONE_NUMBER', () => {
    expect(() =>
      validateButtons([
        { type: 'PHONE_NUMBER', text: 'a', phone_number: '+1' },
        { type: 'PHONE_NUMBER', text: 'b', phone_number: '+2' },
      ]),
    ).toThrow(/At most 1 PHONE_NUMBER/);
  });
  it('rejects > 1 COPY_CODE', () => {
    expect(() =>
      validateButtons([
        { type: 'COPY_CODE', text: 'a', example: 'X' },
        { type: 'COPY_CODE', text: 'b', example: 'Y' },
      ]),
    ).toThrow(/At most 1 COPY_CODE/);
  });
  it('rejects QUICK_REPLY interleaved with CTA buttons', () => {
    expect(() =>
      validateButtons([
        { type: 'QUICK_REPLY', text: 'A' },
        { type: 'URL', text: 'B', url: 'https://x' },
        { type: 'QUICK_REPLY', text: 'C' },
      ]),
    ).toThrow(/cannot be interleaved/);
  });
  it('accepts QUICK_REPLY then CTA in correct order', () => {
    expect(() =>
      validateButtons([
        { type: 'QUICK_REPLY', text: 'A' },
        { type: 'QUICK_REPLY', text: 'B' },
        { type: 'URL', text: 'C', url: 'https://x' },
      ]),
    ).not.toThrow();
  });
  it('rejects empty button text', () => {
    expect(() =>
      validateButtons([{ type: 'QUICK_REPLY', text: '' }]),
    ).toThrow(/missing text/);
  });
  it('rejects URL button without url', () => {
    expect(() =>
      validateButtons([{ type: 'URL', text: 'Go', url: '' }]),
    ).toThrow(/missing url/);
  });
  it('rejects URL button with invalid url', () => {
    expect(() =>
      validateButtons([{ type: 'URL', text: 'Go', url: 'not-a-url' }]),
    ).toThrow(/invalid url/);
  });
  it('rejects URL with {{1}} but no example', () => {
    expect(() =>
      validateButtons([
        { type: 'URL', text: 'Go', url: 'https://x/{{1}}' },
      ]),
    ).toThrow(/Meta requires an example/);
  });
  it('rejects URL with non-{{1}} variable', () => {
    expect(() =>
      validateButtons([
        {
          type: 'URL',
          text: 'Go',
          url: 'https://x/{{2}}',
          example: 'foo',
        },
      ]),
    ).toThrow(/must be \{\{1\}\}/);
  });
  it('rejects PHONE_NUMBER without phone_number', () => {
    expect(() =>
      validateButtons([
        { type: 'PHONE_NUMBER', text: 'Call', phone_number: '' },
      ]),
    ).toThrow(/missing phone_number/);
  });
  it('rejects COPY_CODE without example', () => {
    expect(() =>
      validateButtons([{ type: 'COPY_CODE', text: 'Copy', example: '' }]),
    ).toThrow(/missing example/);
  });
});

describe('validateSampleValues', () => {
  it('rejects mismatched body sample count', () => {
    expect(() =>
      validateSampleValues(
        { ...baseValid, body_text: 'Hi {{1}}', sample_values: { body: [] } },
        1,
        0,
      ),
    ).toThrow(/exactly 1 sample/);
  });
  it('rejects empty sample values', () => {
    expect(() =>
      validateSampleValues(
        { ...baseValid, sample_values: { body: ['  '] } },
        1,
        0,
      ),
    ).toThrow(/empty/);
  });
  it('accepts matching counts', () => {
    expect(() =>
      validateSampleValues(
        { ...baseValid, sample_values: { body: ['John'] } },
        1,
        0,
      ),
    ).not.toThrow();
  });
});

describe('validateTemplatePayload — integration', () => {
  it('passes for a minimal valid payload', () => {
    expect(validateTemplatePayload(baseValid)).toEqual({
      bodyVarCount: 0,
      headerVarCount: 0,
    });
  });
  it('passes with body variables + matching samples', () => {
    expect(
      validateTemplatePayload({
        ...baseValid,
        body_text: 'Hi {{1}}, order {{2}} confirmed.',
        sample_values: { body: ['John', 'ORD-42'] },
      }),
    ).toEqual({ bodyVarCount: 2, headerVarCount: 0 });
  });
  it('throws on missing samples for body variables', () => {
    expect(() =>
      validateTemplatePayload({
        ...baseValid,
        body_text: 'Hi {{1}}',
      }),
    ).toThrow(/exactly 1 sample/);
  });
});
