import { expect, tap } from '@git.zone/tstest/tapbundle';
import { EmailRouter, type IEmailRoute, type IEmailContext } from '../ts/mail/routing/index.js';
import { Email } from '../ts/mail/core/classes.email.js';

tap.test('EmailRouter - should create and manage routes', async () => {
  const router = new EmailRouter([]);
  
  // Test initial state
  expect(router.getRoutes()).toEqual([]);
  
  // Add some test routes
  const routes: IEmailRoute[] = [
    {
      name: 'forward-example',
      priority: 10,
      match: {
        recipients: '*@example.com'
      },
      action: {
        type: 'forward',
        forward: {
          host: 'mail.example.com',
          port: 25
        }
      }
    },
    {
      name: 'reject-spam',
      priority: 20,
      match: {
        senders: '*@spammer.com'
      },
      action: {
        type: 'reject',
        reject: {
          code: 550,
          message: 'Spam not allowed'
        }
      }
    }
  ];
  
  router.updateRoutes(routes);
  expect(router.getRoutes().length).toEqual(2);
});

tap.test('EmailRouter - should evaluate routes based on priority', async () => {
  const router = new EmailRouter([]);
  
  const routes: IEmailRoute[] = [
    {
      name: 'low-priority',
      priority: 5,
      match: {
        recipients: '*@test.com'
      },
      action: {
        type: 'deliver'
      }
    },
    {
      name: 'high-priority',
      priority: 10,
      match: {
        recipients: 'admin@test.com'
      },
      action: {
        type: 'process',
        process: {
          scan: true
        }
      }
    }
  ];
  
  router.updateRoutes(routes);
  
  // Create test context
  const email = new Email({
    from: 'sender@example.com',
    to: 'admin@test.com',
    subject: 'Test email',
    text: 'Test email content'
  });
  
  const context: IEmailContext = {
    email,
    session: {
      id: 'test-session',
      remoteAddress: '192.168.1.1',
      matchedRoute: null
    } as any
  };
  
  const route = await router.evaluateRoutes(context);
  expect(route).not.toEqual(null);
  expect(route?.name).toEqual('high-priority');
});

tap.test('EmailRouter - should match recipient patterns', async () => {
  const router = new EmailRouter([]);
  
  const routes: IEmailRoute[] = [
    {
      name: 'exact-match',
      match: {
        recipients: 'admin@example.com'
      },
      action: {
        type: 'forward',
        forward: {
          host: 'admin-server.com',
          port: 25
        }
      }
    },
    {
      name: 'wildcard-match',
      match: {
        recipients: '*@example.com'
      },
      action: {
        type: 'deliver'
      }
    }
  ];
  
  router.updateRoutes(routes);
  
  // Test exact match
  const email1 = new Email({
    from: 'sender@test.com',
    to: 'admin@example.com',
    subject: 'Admin email',
    text: 'Admin email content'
  });
  
  const context1: IEmailContext = {
    email: email1,
    session: { id: 'test1', remoteAddress: '10.0.0.1' } as any
  };
  
  const route1 = await router.evaluateRoutes(context1);
  expect(route1?.name).toEqual('exact-match');
  
  // Test wildcard match
  const email2 = new Email({
    from: 'sender@test.com',
    to: 'user@example.com',
    subject: 'User email',
    text: 'User email content'
  });
  
  const context2: IEmailContext = {
    email: email2,
    session: { id: 'test2', remoteAddress: '10.0.0.2' } as any
  };
  
  const route2 = await router.evaluateRoutes(context2);
  expect(route2?.name).toEqual('wildcard-match');
});

tap.test('EmailRouter - should match IP ranges with CIDR notation', async () => {
  const router = new EmailRouter([]);
  
  const routes: IEmailRoute[] = [
    {
      name: 'internal-network',
      match: {
        clientIp: '10.0.0.0/24'
      },
      action: {
        type: 'deliver'
      }
    },
    {
      name: 'external-network',
      match: {
        clientIp: ['192.168.1.0/24', '172.16.0.0/16']
      },
      action: {
        type: 'process',
        process: {
          scan: true
        }
      }
    }
  ];
  
  router.updateRoutes(routes);
  
  // Test internal network match
  const email = new Email({
    from: 'internal@company.com',
    to: 'user@company.com',
    subject: 'Internal email',
    text: 'Internal email content'
  });
  
  const context1: IEmailContext = {
    email,
    session: { id: 'test1', remoteAddress: '10.0.0.15' } as any
  };
  
  const route1 = await router.evaluateRoutes(context1);
  expect(route1?.name).toEqual('internal-network');
  
  // Test external network match
  const context2: IEmailContext = {
    email,
    session: { id: 'test2', remoteAddress: '192.168.1.100' } as any
  };
  
  const route2 = await router.evaluateRoutes(context2);
  expect(route2?.name).toEqual('external-network');
});

tap.test('EmailRouter - should handle authentication matching', async () => {
  const router = new EmailRouter([]);
  
  const routes: IEmailRoute[] = [
    {
      name: 'authenticated-users',
      match: {
        authenticated: true
      },
      action: {
        type: 'deliver'
      }
    },
    {
      name: 'unauthenticated-users',
      match: {
        authenticated: false
      },
      action: {
        type: 'reject',
        reject: {
          code: 550,
          message: 'Authentication required'
        }
      }
    }
  ];
  
  router.updateRoutes(routes);
  
  const email = new Email({
    from: 'user@example.com',
    to: 'recipient@test.com',
    subject: 'Test',
    text: 'Test content'
  });
  
  // Test authenticated session
  const context1: IEmailContext = {
    email,
    session: { 
      id: 'test1', 
      remoteAddress: '10.0.0.1',
      authenticated: true,
      authenticatedUser: 'user@example.com'
    } as any
  };
  
  const route1 = await router.evaluateRoutes(context1);
  expect(route1?.name).toEqual('authenticated-users');
  
  // Test unauthenticated session
  const context2: IEmailContext = {
    email,
    session: { 
      id: 'test2', 
      remoteAddress: '10.0.0.2',
      authenticated: false
    } as any
  };
  
  const route2 = await router.evaluateRoutes(context2);
  expect(route2?.name).toEqual('unauthenticated-users');
});

export default tap.start();