import { describe, it, expect, beforeAll, afterAll, beforeEach, afterEach } from 'vitest';
import { createContainer, ContainerManager } from '../../apps/website/lib/di/container';
import {
  SESSION_SERVICE_TOKEN,
  LEAGUE_MEMBERSHIP_SERVICE_TOKEN,
  LEAGUE_SERVICE_TOKEN,
  AUTH_SERVICE_TOKEN,
  DRIVER_SERVICE_TOKEN,
  TEAM_SERVICE_TOKEN,
  RACE_SERVICE_TOKEN,
  DASHBOARD_SERVICE_TOKEN,
  LOGGER_TOKEN,
  CONFIG_TOKEN,
  LEAGUE_API_CLIENT_TOKEN,
  AUTH_API_CLIENT_TOKEN,
  DRIVER_API_CLIENT_TOKEN,
  TEAM_API_CLIENT_TOKEN,
  RACE_API_CLIENT_TOKEN,
} from '../../apps/website/lib/di/tokens';

// Define minimal service interfaces for testing
interface MockSessionService {
  getSession: () => Promise<unknown>;
}

interface MockAuthService {
  login: (email: string, password: string) => Promise<unknown>;
}

interface MockLeagueService {
  getAllLeagues: () => Promise<unknown[]>;
}

interface MockLeagueMembershipService {
  getLeagueMemberships: (userId: string) => Promise<unknown[]>;
}

interface ConfigFunction {
  (): string;
}

/**
 * Integration test for website DI container
 */
describe('Website DI Container Integration', () => {
  let originalEnv: NodeJS.ProcessEnv;

  beforeAll(() => {
    originalEnv = { ...process.env };
    process.env.NODE_ENV = 'test';
  });

  afterAll(() => {
    process.env = originalEnv;
    ContainerManager.getInstance().dispose();
  });

  beforeEach(() => {
    // Clean up all API URL env vars before each test
    delete process.env.API_BASE_URL;
    delete process.env.NEXT_PUBLIC_API_BASE_URL;
    ContainerManager.getInstance().dispose();
  });

  afterEach(() => {
    // Clean up after each test
    delete process.env.API_BASE_URL;
    delete process.env.NEXT_PUBLIC_API_BASE_URL;
    ContainerManager.getInstance().dispose();
  });

  describe('Basic Container Functionality', () => {
    it('creates container successfully', () => {
      process.env.API_BASE_URL = 'http://localhost:3001';
      const container = createContainer();
      expect(container).toBeDefined();
      expect(container).not.toBeNull();
    });

    it('resolves core services without errors', () => {
      process.env.API_BASE_URL = 'http://localhost:3001';
      const container = createContainer();
      
      expect(() => container.get(LOGGER_TOKEN)).not.toThrow();
      expect(() => container.get(CONFIG_TOKEN)).not.toThrow();
      
      const logger = container.get(LOGGER_TOKEN);
      const config = container.get(CONFIG_TOKEN);
      
      expect(logger).toBeDefined();
      expect(config).toBeDefined();
    });

    it('resolves API clients without errors', () => {
      process.env.API_BASE_URL = 'http://localhost:3001';
      const container = createContainer();
      
      const apiClients = [
        LEAGUE_API_CLIENT_TOKEN,
        AUTH_API_CLIENT_TOKEN,
        DRIVER_API_CLIENT_TOKEN,
        TEAM_API_CLIENT_TOKEN,
        RACE_API_CLIENT_TOKEN,
      ];

      for (const token of apiClients) {
        expect(() => container.get(token)).not.toThrow();
        const client = container.get(token);
        expect(client).toBeDefined();
      }
    });

    it('resolves auth services including SessionService', () => {
      process.env.API_BASE_URL = 'http://localhost:3001';
      const container = createContainer();
      
      expect(() => container.get(SESSION_SERVICE_TOKEN)).not.toThrow();
      expect(() => container.get(AUTH_SERVICE_TOKEN)).not.toThrow();
      
      const sessionService = container.get<MockSessionService>(SESSION_SERVICE_TOKEN);
      const authService = container.get<MockAuthService>(AUTH_SERVICE_TOKEN);
      
      expect(sessionService).toBeDefined();
      expect(authService).toBeDefined();
      expect(typeof sessionService.getSession).toBe('function');
      expect(typeof authService.login).toBe('function');
    });

    it('resolves league services including LeagueMembershipService', () => {
      process.env.API_BASE_URL = 'http://localhost:3001';
      const container = createContainer();
      
      expect(() => container.get(LEAGUE_SERVICE_TOKEN)).not.toThrow();
      expect(() => container.get(LEAGUE_MEMBERSHIP_SERVICE_TOKEN)).not.toThrow();
      
      const leagueService = container.get<MockLeagueService>(LEAGUE_SERVICE_TOKEN);
      const membershipService = container.get<MockLeagueMembershipService>(LEAGUE_MEMBERSHIP_SERVICE_TOKEN);
      
      expect(leagueService).toBeDefined();
      expect(membershipService).toBeDefined();
      expect(typeof leagueService.getAllLeagues).toBe('function');
      expect(typeof membershipService.getLeagueMemberships).toBe('function');
    });

    it('resolves domain services without errors', () => {
      process.env.API_BASE_URL = 'http://localhost:3001';
      const container = createContainer();
      
      expect(() => container.get(DRIVER_SERVICE_TOKEN)).not.toThrow();
      expect(() => container.get(TEAM_SERVICE_TOKEN)).not.toThrow();
      expect(() => container.get(RACE_SERVICE_TOKEN)).not.toThrow();
      expect(() => container.get(DASHBOARD_SERVICE_TOKEN)).not.toThrow();
      
      const driverService = container.get(DRIVER_SERVICE_TOKEN);
      const teamService = container.get(TEAM_SERVICE_TOKEN);
      const raceService = container.get(RACE_SERVICE_TOKEN);
      const dashboardService = container.get(DASHBOARD_SERVICE_TOKEN);
      
      expect(driverService).toBeDefined();
      expect(teamService).toBeDefined();
      expect(raceService).toBeDefined();
      expect(dashboardService).toBeDefined();
    });

    it('resolves all services in a single operation', () => {
      process.env.API_BASE_URL = 'http://localhost:3001';
      const container = createContainer();
      
      const tokens = [
        SESSION_SERVICE_TOKEN,
        LEAGUE_MEMBERSHIP_SERVICE_TOKEN,
        LEAGUE_SERVICE_TOKEN,
        AUTH_SERVICE_TOKEN,
        DRIVER_SERVICE_TOKEN,
        TEAM_SERVICE_TOKEN,
        RACE_SERVICE_TOKEN,
        DASHBOARD_SERVICE_TOKEN,
        LOGGER_TOKEN,
        CONFIG_TOKEN,
      ];

      const services = tokens.map(token => {
        try {
          return container.get(token);
        } catch (error) {
          throw new Error(`Failed to resolve token ${token.toString()}: ${error.message}`);
        }
      });

      expect(services.length).toBe(tokens.length);
      services.forEach((service) => {
        expect(service).toBeDefined();
        expect(service).not.toBeNull();
      });
    });

    it('throws clear error for non-existent bindings', () => {
      process.env.API_BASE_URL = 'http://localhost:3001';
      const container = createContainer();
      
      const fakeToken = Symbol.for('Non.Existent.Service');
      expect(() => container.get(fakeToken)).toThrow();
    });
  });

  describe('SSR Dynamic Environment Variables', () => {
    it('config binding is a function (not a string)', () => {
      process.env.API_BASE_URL = 'http://test:3001';
      
      const container = createContainer();
      const config = container.get(CONFIG_TOKEN);
      
      // Config should be a function that can be called
      expect(typeof config).toBe('function');
      
      // Should be callable
      const configFn = config as ConfigFunction;
      expect(() => configFn()).not.toThrow();
    });

    it('config function returns current environment value', () => {
      process.env.API_BASE_URL = 'http://test:3001';
      
      const container = createContainer();
      const getConfig = container.get(CONFIG_TOKEN) as ConfigFunction;
      
      const configValue = getConfig();
      // Should return some value (could be fallback in test env)
      expect(typeof configValue).toBe('string');
      expect(configValue.length).toBeGreaterThan(0);
    });

    it('multiple containers share the same config behavior', () => {
      process.env.API_BASE_URL = 'http://test:3001';
      
      const container1 = createContainer();
      const container2 = createContainer();
      
      const config1 = container1.get(CONFIG_TOKEN) as ConfigFunction;
      const config2 = container2.get(CONFIG_TOKEN) as ConfigFunction;
      
      // Both should be functions
      expect(typeof config1).toBe('function');
      expect(typeof config2).toBe('function');
      
      // Both should return the same type of value
      expect(typeof config1()).toBe(typeof config2());
    });

    it('ContainerManager singleton behavior', () => {
      process.env.API_BASE_URL = 'http://test:3001';
      
      const manager = ContainerManager.getInstance();
      const container1 = manager.getContainer();
      const container2 = manager.getContainer();
      
      // Should be same instance
      expect(container1).toBe(container2);
      
      const config1 = container1.get(CONFIG_TOKEN) as ConfigFunction;
      const config2 = container2.get(CONFIG_TOKEN) as ConfigFunction;
      
      // Both should be functions
      expect(typeof config1).toBe('function');
      expect(typeof config2).toBe('function');
    });

    it('API clients work with dynamic config', () => {
      process.env.API_BASE_URL = 'http://api-test:3001';
      
      const container = createContainer();
      const leagueClient = container.get(LEAGUE_API_CLIENT_TOKEN);
      
      expect(leagueClient).toBeDefined();
      expect(leagueClient).not.toBeNull();
      expect(typeof leagueClient).toBe('object');
    });

    it('dispose clears singleton instance', () => {
      process.env.API_BASE_URL = 'http://test:3001';
      
      const manager = ContainerManager.getInstance();
      const container1 = manager.getContainer();
      
      manager.dispose();
      
      const container2 = manager.getContainer();
      
      // Should be different instances after dispose
      expect(container1).not.toBe(container2);
    });

    it('services work after container recreation', () => {
      process.env.API_BASE_URL = 'http://test:3001';
      
      const container1 = createContainer();
      const config1 = container1.get(CONFIG_TOKEN) as ConfigFunction;
      
      ContainerManager.getInstance().dispose();
      
      const container2 = createContainer();
      const config2 = container2.get(CONFIG_TOKEN) as ConfigFunction;
      
      // Both should be functions
      expect(typeof config1).toBe('function');
      expect(typeof config2).toBe('function');
    });
  });

  describe('SSR Boot Safety', () => {
    it('resolves all tokens required for SSR entry rendering', () => {
      process.env.API_BASE_URL = 'http://localhost:3001';
      const container = createContainer();
      
      // Tokens typically used in SSR (middleware, layouts, initial page loads)
      const ssrTokens = [
        LOGGER_TOKEN,
        CONFIG_TOKEN,
        SESSION_SERVICE_TOKEN,
        AUTH_SERVICE_TOKEN,
        LEAGUE_SERVICE_TOKEN,
        DRIVER_SERVICE_TOKEN,
        DASHBOARD_SERVICE_TOKEN,
        // API clients are often resolved by services
        AUTH_API_CLIENT_TOKEN,
        LEAGUE_API_CLIENT_TOKEN,
      ];

      for (const token of ssrTokens) {
        try {
          const service = container.get(token);
          expect(service, `Failed to resolve ${token.toString()}`).toBeDefined();
        } catch (error) {
          throw new Error(`SSR Boot Safety Failure: Could not resolve ${token.toString()}. Error: ${error.message}`);
        }
      }
    });
  });
});
