import { test } from 'node:test';
import assert from 'node:assert';
import { app } from '../app.js';
import { pool } from '../db/connection.js';
import { encryptText } from '../services/encryption.js';

// Mock user for testing
const mockUser = {
  userId: 1,
  email: 'test@example.com'
};

// Helper to create a test request
function createTestRequest(path, method, body = {}, headers = {}) {
  return {
    method,
    path,
    headers: {
      'Content-Type': 'application/json',
      ...headers
    },
    body
  };
}

test('POST /addresses/change-request - valid address change request', async () => {
  // Mock existing address in DB
  await pool.query(
    `INSERT INTO addresses (user_id, address_encrypted, postal_verified_at)
     VALUES (?, ?, CURRENT_TIMESTAMP)`,
    [mockUser.userId, encryptText('Old Address')]
  );

  const req = createTestRequest('/addresses/change-request', 'POST', {
    newAddress: 'New Test Address'
  });

  const res = await app.request(req);

  assert.strictEqual(res.status, 201);
  const data = await res.json();
  assert.ok(data.requestId);
  assert.strictEqual(data.postalDispatch, 'pending_letter');
  assert.ok(data.verificationCode);
});

test('POST /addresses/change-request - invalid address (too short)', async () => {
  const req = createTestRequest('/addresses/change-request', 'POST', {
    newAddress: 'Short'
  });

  const res = await app.request(req);

  assert.strictEqual(res.status, 400);
  const data = await res.json();
  assert.ok(data.error);
});

test('POST /addresses/verify - valid verification', async () => {
  // Mock a change request first
  const code = '123456';
  const codeHash = 'hashed_code';
  await pool.query(
    `INSERT INTO address_change_requests (user_id, new_address_encrypted, verification_code_hash, status)
     VALUES (?, ?, ?, 'pending_letter')`,
    [mockUser.userId, encryptText('New Address'), codeHash]
  );

  const req = createTestRequest('/addresses/verify', 'POST', {
    requestId: 1,
    code
  });

  const res = await app.request(req);

  assert.strictEqual(res.status, 200);
  const data = await res.json();
  assert.strictEqual(data.status, 'verified');
});

test('POST /addresses/verify - invalid verification code', async () => {
  const req = createTestRequest('/addresses/verify', 'POST', {
    requestId: 1,
    code: 'wrongcode'
  });

  const res = await app.request(req);

  assert.strictEqual(res.status, 400);
  const data = await res.json();
  assert.ok(data.error);
});

test('POST /addresses/change-request - no existing address', async () => {
  // Ensure no existing address for this user
  await pool.query(
    `DELETE FROM addresses WHERE user_id = ?`,
    [mockUser.userId]
  );

  const req = createTestRequest('/addresses/change-request', 'POST', {
    newAddress: 'New Address'
  });

  const res = await app.request(req);

  assert.strictEqual(res.status, 400);
  const data = await res.json();
  assert.ok(data.error);
});

test('POST /addresses/verify - request not found', async () => {
  const req = createTestRequest('/addresses/verify', 'POST', {
    requestId: 999,
    code: '123456'
  });

  const res = await app.request(req);

  assert.strictEqual(res.status, 404);
  const data = await res.json();
  assert.ok(data.error);
});

test('POST /addresses/verify - wrong user', async () => {
  // Mock a change request first
  const code = '123456';
  const codeHash = 'hashed_code';
  await pool.query(
    `INSERT INTO address_change_requests (user_id, new_address_encrypted, verification_code_hash, status)
     VALUES (?, ?, ?, 'pending_letter')`,
    [999, encryptText('New Address'), codeHash] // Different user ID
  );

  const req = createTestRequest('/addresses/verify', 'POST', {
    requestId: 1,
    code
  });

  const res = await app.request(req);

  assert.strictEqual(res.status, 403);
  const data = await res.json();
  assert.ok(data.error);
});

test('POST /addresses/verify - request not pending', async () => {
  // Mock a change request first with status 'verified'
  const code = '123456';
  const codeHash = 'hashed_code';
  await pool.query(
    `INSERT INTO address_change_requests (user_id, new_address_encrypted, verification_code_hash, status)
     VALUES (?, ?, ?, 'verified')`,
    [mockUser.userId, encryptText('New Address'), codeHash]
  );

  const req = createTestRequest('/addresses/verify', 'POST', {
    requestId: 1,
    code
  });

  const res = await app.request(req);

  assert.strictEqual(res.status, 409);
  const data = await res.json();
  assert.ok(data.error);
});

// Additional test for error handling in change-request route
test('POST /addresses/change-request - database error', async () => {
  // Mock existing address in DB
  await pool.query(
    `INSERT INTO addresses (user_id, address_encrypted, postal_verified_at)
     VALUES (?, ?, CURRENT_TIMESTAMP)`,
    [mockUser.userId, encryptText('Old Address')]
  );

  // Temporarily disable the database to simulate an error
  const originalQuery = pool.query;
  pool.query = async () => { throw new Error('Database error'); };

  const req = createTestRequest('/addresses/change-request', 'POST', {
    newAddress: 'New Test Address'
  });

  const res = await app.request(req);

  assert.strictEqual(res.status, 500);
  const data = await res.json();
  assert.ok(data.error);

  // Restore the original query function
  pool.query = originalQuery;
});

// Additional test for error handling in verify route
test('POST /addresses/verify - database error during transaction', async () => {
  // Mock a change request first
  const code = '123456';
  const codeHash = 'hashed_code';
  await pool.query(
    `INSERT INTO address_change_requests (user_id, new_address_encrypted, verification_code_hash, status)
     VALUES (?, ?, ?, 'pending_letter')`,
    [mockUser.userId, encryptText('New Address'), codeHash]
  );

  // Temporarily disable the database to simulate an error
  const originalQuery = pool.query;
  pool.query = async () => { throw new Error('Database error'); };

  const req = createTestRequest('/addresses/verify', 'POST', {
    requestId: 1,
    code
  });

  const res = await app.request(req);

  assert.strictEqual(res.status, 500);
  const data = await res.json();
  assert.ok(data.error);

  // Restore the original query function
  pool.query = originalQuery;
});