import { describe, it, expect, beforeEach, afterAll } from 'vitest';
import { unlinkSync, existsSync, mkdirSync } from 'node:fs';
import { resolve, dirname } from 'node:path';
import { getDb, closeDb, resetDbForTests } from '../src/db/index.js';
import { intakeService, blacklistService, extensionAuthService } from '../src/services/intake.service.js';
import { ingestSchema } from '../src/schemas.js';

const TEST_DB = resolve(process.cwd(), 'data/test-ticket-gate.db');

process.env.DATABASE_PATH = TEST_DB;
process.env.MAJ_WP_API_KEY = 'test-maj-wp-key';
process.env.SESSION_SECRET = 'test-session';
process.env.ADMIN_PASSWORD = 'test-admin';
process.env.NODE_ENV = 'test';

beforeEach(() => {
  if (existsSync(TEST_DB)) unlinkSync(TEST_DB);
  mkdirSync(dirname(TEST_DB), { recursive: true });
  closeDb();
  getDb();
  resetDbForTests();
});

afterAll(() => {
  closeDb();
  if (existsSync(TEST_DB)) unlinkSync(TEST_DB);
});

describe('Ingestion', () => {
  it('ingests a new demand', () => {
    const result = intakeService.ingest({
      source_type: 'demo',
      source_external_id: 'test-001',
      title: 'Test ticket',
      body: 'Contenu test',
      sender_email: 'test@example.com',
    });
    expect(result.created).toBe(true);
    expect(result.item.decision).toBe('pending');
    expect(result.item.title).toBe('Test ticket');
  });

  it('deduplicates same source external id', () => {
    const input = {
      source_type: 'gmail' as const,
      source_external_id: 'msg-123',
      title: 'Email 1',
      sender_email: 'a@b.com',
    };
    const first = intakeService.ingest(input);
    const second = intakeService.ingest(input);
    expect(first.created).toBe(true);
    expect(second.duplicate).toBe(true);
    expect(second.item.id).toBe(first.item.id);
  });

  it('deduplicates monday chrome vs api same item', () => {
    const base = {
      source_external_id: 'monday-123-456',
      title: 'Monday item',
    };
    intakeService.ingest({ ...base, source_type: 'monday_api', source_account: '123' });
    const chrome = intakeService.ingest({ ...base, source_type: 'monday_chrome', source_account: '123' });
    expect(chrome.duplicate).toBe(true);
  });
});

describe('Validation workflow', () => {
  it('approves pending item', () => {
    const { item } = intakeService.ingest({
      source_type: 'demo',
      source_external_id: 'approve-1',
      title: 'To approve',
    });
    const approved = intakeService.approve(item.id);
    expect(approved?.decision).toBe('approved');
    expect(approved?.status).toBe('approved');
  });

  it('ignores pending item', () => {
    const { item } = intakeService.ingest({
      source_type: 'demo',
      source_external_id: 'ignore-1',
      title: 'To ignore',
    });
    const ignored = intakeService.ignore(item.id);
    expect(ignored?.decision).toBe('ignored');
  });

  it('restores ignored item', () => {
    const { item } = intakeService.ingest({
      source_type: 'demo',
      source_external_id: 'restore-1',
      title: 'Restore me',
    });
    intakeService.ignore(item.id);
    const restored = intakeService.restore(item.id);
    expect(restored?.decision).toBe('pending');
  });
});

describe('Blacklist', () => {
  it('auto-ignores blacklisted sender on ingest', () => {
    blacklistService.addSender({
      sender_type: 'email',
      sender_key: 'spam@evil.com',
      sender_label: 'Spammer',
    });
    const result = intakeService.ingest({
      source_type: 'gmail',
      source_external_id: 'spam-001',
      title: 'Spam',
      sender_email: 'spam@evil.com',
    });
    expect(result.item.decision).toBe('auto_ignored');
  });

  it('blacklists sender from ignored item', () => {
    const { item } = intakeService.ingest({
      source_type: 'demo',
      source_external_id: 'bl-1',
      title: 'Block sender',
      sender_email: 'block@example.com',
    });
    intakeService.ignore(item.id);
    const sender = blacklistService.ignoreSenderFromItem(item.id);
    expect(sender?.sender_key).toBe('block@example.com');
  });
});

describe('maj-wp integration', () => {
  it('lists ready approved items', () => {
    const { item } = intakeService.ingest({
      source_type: 'demo',
      source_external_id: 'ready-1',
      title: 'Ready',
    });
    intakeService.approve(item.id);
    const ready = intakeService.listReady();
    expect(ready.some((i) => i.id === item.id)).toBe(true);
  });

  it('claims item atomically', () => {
    const { item } = intakeService.ingest({
      source_type: 'demo',
      source_external_id: 'claim-1',
      title: 'Claim me',
    });
    intakeService.approve(item.id);
    const claim = intakeService.claim(item.id, 'maj-wp-1');
    expect(claim.ok).toBe(true);
    if (claim.ok) {
      expect(claim.item.status).toBe('claimed');
      expect(claim.claim_token).toBeTruthy();
    }
  });

  it('prevents concurrent double claim', () => {
    const { item } = intakeService.ingest({
      source_type: 'demo',
      source_external_id: 'claim-2',
      title: 'No double claim',
    });
    intakeService.approve(item.id);
    const c1 = intakeService.claim(item.id, 'consumer-a');
    expect(c1.ok).toBe(true);
    const c2 = intakeService.claim(item.id, 'consumer-b');
    expect(c2.ok).toBe(false);
    if (!c2.ok) expect(c2.reason).toBe('already_claimed');
  });

  it('acknowledges creation', () => {
    const { item } = intakeService.ingest({
      source_type: 'demo',
      source_external_id: 'created-1',
      title: 'Create',
    });
    intakeService.approve(item.id);
    const claim = intakeService.claim(item.id, 'maj-wp');
    expect(claim.ok).toBe(true);
    const created = intakeService.acknowledgeCreated(item.id, {
      claim_token: claim.ok ? claim.claim_token : undefined,
      maj_wp_ticket_id: 'WP-42',
      remote_status: 'open',
      remote_message: 'Ticket créé',
    });
    expect(created?.status).toBe('created');
    expect(created?.maj_wp_ticket_id).toBe('WP-42');
  });

  it('is idempotent on double acknowledgment', () => {
    const { item } = intakeService.ingest({
      source_type: 'demo',
      source_external_id: 'idempotent-1',
      title: 'Idempotent',
    });
    intakeService.approve(item.id);
    intakeService.claim(item.id, 'maj-wp');
    intakeService.acknowledgeCreated(item.id, { maj_wp_ticket_id: 'WP-99' });
    const second = intakeService.acknowledgeCreated(item.id, { maj_wp_ticket_id: 'WP-99' });
    expect(second?.maj_wp_ticket_id).toBe('WP-99');
    expect(second?.status).toBe('created');
  });

  it('updates remote status and error', () => {
    const { item } = intakeService.ingest({
      source_type: 'demo',
      source_external_id: 'status-1',
      title: 'Status',
    });
    intakeService.approve(item.id);
    intakeService.acknowledgeCreated(item.id, { maj_wp_ticket_id: 'WP-1' });

    const updated = intakeService.updateRemoteStatus(item.id, {
      status: 'failed',
      remote_status: 'error',
      remote_message: 'Échec création',
      error: 'Timeout API',
    });
    expect(updated?.status).toBe('failed');
    expect(updated?.error).toBe('Timeout API');
  });

  it('claim-next returns first ready item', () => {
    intakeService.ingest({ source_type: 'demo', source_external_id: 'cn-1', title: 'First' });
    const i2 = intakeService.ingest({ source_type: 'demo', source_external_id: 'cn-2', title: 'Second' });
    intakeService.approve(i2.item.id);
    const result = intakeService.claimNextReady('maj-wp');
    expect(result.ok).toBe(true);
  });
});

describe('Extension auth', () => {
  it('pairs extension with code', () => {
    const { code } = extensionAuthService.createPairingCode();
    const result = extensionAuthService.redeemPairingCode(code);
    expect(result?.token).toBeTruthy();
    expect(extensionAuthService.validateExtensionToken(result!.token)).toBe(true);
  });

  it('rejects invalid extension token', () => {
    expect(extensionAuthService.validateExtensionToken('invalid-token')).toBe(false);
  });
});

describe('Payload validation', () => {
  it('rejects invalid ingest payload', () => {
    const result = ingestSchema.safeParse({ source_type: 'demo', title: '' });
    expect(result.success).toBe(false);
  });

  it('accepts valid monday chrome payload', () => {
    const result = ingestSchema.safeParse({
      source_type: 'monday_chrome',
      source_external_id: 'monday-1-2',
      title: 'Valid',
      body: 'Content',
    });
    expect(result.success).toBe(true);
  });
});

describe('Extension ingest flow', () => {
  it('simulates extension payload ingestion', () => {
    const payload = {
      source_type: 'monday_chrome' as const,
      source_account: '123',
      source_external_id: 'monday-123-456',
      source_url: 'https://foo.monday.com/boards/123/pulses/456',
      title: 'From extension',
      body: 'Description',
      sender_name: 'User',
      metadata: { board_id: '123', item_id: '456' },
      parse_warnings: ['Description non trouvée'],
    };
    const r1 = intakeService.ingest(payload);
    expect(r1.created).toBe(true);
    const r2 = intakeService.ingest(payload);
    expect(r2.duplicate).toBe(true);
  });
});
