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

tap.test('Email Integration - Route-based forwarding scenario', async () => {
  // Define routes with match/action pattern
  const routes: IEmailRoute[] = [
    {
      name: 'office-relay',
      priority: 100,
      match: { 
        clientIp: '192.168.0.0/16' 
      },
      action: { 
        type: 'forward',
        forward: { 
          host: 'internal.mail.example.com',
          port: 25 
        }
      }
    },
    {
      name: 'company-mail',
      priority: 50,
      match: { 
        recipients: '*@mycompany.com' 
      },
      action: { 
        type: 'process',
        process: { 
          scan: true,
          dkim: true,
          queue: 'normal' 
        }
      }
    },
    {
      name: 'admin-priority',
      priority: 90,
      match: { 
        recipients: 'admin@mycompany.com' 
      },
      action: { 
        type: 'process',
        process: { 
          scan: true,
          dkim: true,
          queue: 'priority' 
        }
      }
    },
    {
      name: 'spam-reject',
      priority: 80,
      match: { 
        senders: '*@spammer.com' 
      },
      action: { 
        type: 'reject',
        reject: { 
          code: 550,
          message: 'Sender blocked' 
        }
      }
    },
    {
      name: 'default-reject',
      priority: 1,
      match: { 
        recipients: '*' 
      },
      action: { 
        type: 'reject',
        reject: { 
          code: 550,
          message: 'Relay denied' 
        }
      }
    }
  ];

  // Create email router with routes
  const emailRouter = new EmailRouter(routes);

  // Test route priority sorting
  const sortedRoutes = emailRouter.getRoutes();
  expect(sortedRoutes[0].name).toEqual('office-relay'); // Highest priority (100)
  expect(sortedRoutes[1].name).toEqual('admin-priority'); // Priority 90
  expect(sortedRoutes[2].name).toEqual('spam-reject'); // Priority 80
  expect(sortedRoutes[sortedRoutes.length - 1].name).toEqual('default-reject'); // Lowest priority (1)

  // Test route evaluation with different scenarios
  const testCases = [
    {
      description: 'Office relay scenario (IP-based)',
      email: new Email({
        from: 'user@external.com',
        to: 'anyone@anywhere.com',
        subject: 'Test from office',
        text: 'Test message'
      }),
      session: {
        id: 'test-1',
        remoteAddress: '192.168.1.100'
      },
      expectedRoute: 'office-relay'
    },
    {
      description: 'Admin priority mail',
      email: new Email({
        from: 'user@external.com',
        to: 'admin@mycompany.com',
        subject: 'Important admin message',
        text: 'Admin message content'
      }),
      session: {
        id: 'test-2',
        remoteAddress: '10.0.0.1'
      },
      expectedRoute: 'admin-priority'
    },
    {
      description: 'Company mail processing',
      email: new Email({
        from: 'partner@partner.com',
        to: 'sales@mycompany.com',
        subject: 'Business proposal',
        text: 'Business content'
      }),
      session: {
        id: 'test-3',
        remoteAddress: '203.0.113.1'
      },
      expectedRoute: 'company-mail'
    },
    {
      description: 'Spam rejection',
      email: new Email({
        from: 'bad@spammer.com',
        to: 'victim@mycompany.com',
        subject: 'Spam message',
        text: 'Spam content'
      }),
      session: {
        id: 'test-4',
        remoteAddress: '203.0.113.2'
      },
      expectedRoute: 'spam-reject'
    },
    {
      description: 'Default rejection',
      email: new Email({
        from: 'unknown@unknown.com',
        to: 'random@random.com',
        subject: 'Random message',
        text: 'Random content'
      }),
      session: {
        id: 'test-5',
        remoteAddress: '203.0.113.3'
      },
      expectedRoute: 'default-reject'
    }
  ];

  for (const testCase of testCases) {
    const context = {
      email: testCase.email,
      session: testCase.session as any
    };

    const matchedRoute = await emailRouter.evaluateRoutes(context);
    expect(matchedRoute).not.toEqual(null);
    expect(matchedRoute?.name).toEqual(testCase.expectedRoute);
    
    console.log(`✓ ${testCase.description}: Matched route '${matchedRoute?.name}'`);
  }
});

tap.test('Email Integration - CIDR IP matching', async () => {
  const routes: IEmailRoute[] = [
    {
      name: 'internal-network',
      match: { clientIp: ['10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16'] },
      action: { type: 'deliver' }
    },
    {
      name: 'specific-subnet',
      priority: 10,
      match: { clientIp: '192.168.1.0/24' },
      action: { type: 'forward', forward: { host: 'subnet-mail.com', port: 25 } }
    }
  ];

  const emailRouter = new EmailRouter(routes);

  const testIps = [
    { ip: '192.168.1.100', expectedRoute: 'specific-subnet' }, // More specific match
    { ip: '192.168.2.100', expectedRoute: 'internal-network' }, // General internal
    { ip: '10.5.10.20', expectedRoute: 'internal-network' },
    { ip: '172.16.5.10', expectedRoute: 'internal-network' }
  ];

  for (const testCase of testIps) {
    const context = {
      email: new Email({ from: 'test@test.com', to: 'user@test.com', subject: 'Test', text: 'Test' }),
      session: { id: 'test', remoteAddress: testCase.ip } as any
    };

    const route = await emailRouter.evaluateRoutes(context);
    expect(route?.name).toEqual(testCase.expectedRoute);
    console.log(`✓ IP ${testCase.ip}: Matched route '${route?.name}'`);
  }
});

tap.test('Email Integration - Authentication-based routing', async () => {
  const routes: IEmailRoute[] = [
    {
      name: 'authenticated-relay',
      priority: 100,
      match: { authenticated: true },
      action: { 
        type: 'forward',
        forward: { host: 'relay.example.com', port: 587 }
      }
    },
    {
      name: 'unauthenticated-local',
      match: { 
        authenticated: false,
        recipients: '*@localserver.com' 
      },
      action: { type: 'deliver' }
    },
    {
      name: 'unauthenticated-reject',
      match: { authenticated: false },
      action: { 
        type: 'reject',
        reject: { code: 550, message: 'Authentication required' }
      }
    }
  ];

  const emailRouter = new EmailRouter(routes);

  // Test authenticated user
  const authContext = {
    email: new Email({ from: 'user@anywhere.com', to: 'dest@anywhere.com', subject: 'Test', text: 'Test' }),
    session: { 
      id: 'auth-test',
      remoteAddress: '203.0.113.1',
      authenticated: true,
      authenticatedUser: 'user@anywhere.com'
    } as any
  };

  const authRoute = await emailRouter.evaluateRoutes(authContext);
  expect(authRoute?.name).toEqual('authenticated-relay');

  // Test unauthenticated local delivery
  const localContext = {
    email: new Email({ from: 'external@external.com', to: 'user@localserver.com', subject: 'Test', text: 'Test' }),
    session: { 
      id: 'local-test',
      remoteAddress: '203.0.113.2',
      authenticated: false
    } as any
  };

  const localRoute = await emailRouter.evaluateRoutes(localContext);
  expect(localRoute?.name).toEqual('unauthenticated-local');

  // Test unauthenticated rejection
  const rejectContext = {
    email: new Email({ from: 'external@external.com', to: 'user@external.com', subject: 'Test', text: 'Test' }),
    session: { 
      id: 'reject-test',
      remoteAddress: '203.0.113.3',
      authenticated: false
    } as any
  };

  const rejectRoute = await emailRouter.evaluateRoutes(rejectContext);
  expect(rejectRoute?.name).toEqual('unauthenticated-reject');

  console.log('✓ Authentication-based routing works correctly');
});

tap.test('Email Integration - Pattern caching performance', async () => {
  const routes: IEmailRoute[] = [
    {
      name: 'complex-pattern',
      match: { 
        recipients: ['*@domain1.com', '*@domain2.com', 'admin@*.domain3.com'],
        senders: 'partner-*@*.partner.net'
      },
      action: { type: 'forward', forward: { host: 'partner-relay.com', port: 25 } }
    }
  ];

  const emailRouter = new EmailRouter(routes);

  const email = new Email({ 
    from: 'partner-sales@us.partner.net', 
    to: 'admin@sales.domain3.com', 
    subject: 'Test', 
    text: 'Test' 
  });

  const context = {
    email,
    session: { id: 'perf-test', remoteAddress: '10.0.0.1' } as any
  };

  // First evaluation - should populate cache
  const start1 = Date.now();
  const route1 = await emailRouter.evaluateRoutes(context);
  const time1 = Date.now() - start1;

  // Second evaluation - should use cache
  const start2 = Date.now();
  const route2 = await emailRouter.evaluateRoutes(context);
  const time2 = Date.now() - start2;

  expect(route1?.name).toEqual('complex-pattern');
  expect(route2?.name).toEqual('complex-pattern');
  
  // Cache should make second evaluation faster (though this is timing-dependent)
  console.log(`✓ Pattern caching: First evaluation: ${time1}ms, Second: ${time2}ms`);
});

tap.test('Email Integration - Route update functionality', async () => {
  const initialRoutes: IEmailRoute[] = [
    {
      name: 'test-route',
      match: { recipients: '*@test.com' },
      action: { type: 'deliver' }
    }
  ];

  const emailRouter = new EmailRouter(initialRoutes);

  // Test initial configuration
  expect(emailRouter.getRoutes().length).toEqual(1);
  expect(emailRouter.getRoutes()[0].name).toEqual('test-route');

  // Update routes
  const newRoutes: IEmailRoute[] = [
    {
      name: 'updated-route',
      match: { recipients: '*@updated.com' },
      action: { type: 'forward', forward: { host: 'new-server.com', port: 25 } }
    },
    {
      name: 'additional-route',
      match: { recipients: '*@additional.com' },
      action: { type: 'reject', reject: { code: 550, message: 'Blocked' } }
    }
  ];

  emailRouter.updateRoutes(newRoutes);

  // Verify routes were updated
  expect(emailRouter.getRoutes().length).toEqual(2);
  expect(emailRouter.getRoutes()[0].name).toEqual('updated-route');
  expect(emailRouter.getRoutes()[1].name).toEqual('additional-route');

  console.log('✓ Route update functionality works correctly');
});

tap.test('stop', async () => {
  await tap.stopForcefully();
});

export default tap.start();