import { describe, it, expect, beforeAll, afterAll, vi } from 'vitest';
import request from 'supertest';
import express from 'express';
import cors from 'cors';
import helmet from 'helmet';

interface ApiResponse<T> {
  success: boolean;
  data?: T;
  error?: string;
  traceId: string;
  tenantId: string;
}

interface Product {
  id: string;
  name: string;
  sku: string;
  price: number;
  stock: number;
  status: string;
}

interface Order {
  id: string;
  orderId: string;
  status: string;
  totalAmount: number;
  items: Array<{
    sku: string;
    quantity: number;
    price: number;
  }>;
}

interface Customer {
  id: string;
  name: string;
  email: string;
  tier: string;
}

const createTestApp = () => {
  const app = express();
  app.use(helmet());
  app.use(cors());
  app.use(express.json());

  app.use((req, res, next) => {
    req.headers['x-tenant-id'] = req.headers['x-tenant-id'] || 'test-tenant';
    req.headers['x-trace-id'] = req.headers['x-trace-id'] || `trace-${Date.now()}`;
    next();
  });

  app.get('/api/v1/products', async (req, res) => {
    const products: Product[] = [
      { id: '1', name: 'Test Product 1', sku: 'SKU-001', price: 99.99, stock: 100, status: 'ACTIVE' },
      { id: '2', name: 'Test Product 2', sku: 'SKU-002', price: 149.99, stock: 50, status: 'ACTIVE' },
    ];
    res.json({
      success: true,
      data: products,
      traceId: req.headers['x-trace-id'],
      tenantId: req.headers['x-tenant-id'],
    } as ApiResponse<Product[]>);
  });

  app.get('/api/v1/products/:id', async (req, res) => {
    const product: Product = {
      id: req.params.id,
      name: 'Test Product',
      sku: 'SKU-001',
      price: 99.99,
      stock: 100,
      status: 'ACTIVE',
    };
    res.json({
      success: true,
      data: product,
      traceId: req.headers['x-trace-id'],
      tenantId: req.headers['x-tenant-id'],
    } as ApiResponse<Product>);
  });

  app.post('/api/v1/orders', async (req, res) => {
    const order: Order = {
      id: 'order-001',
      orderId: `ORD-${Date.now()}`,
      status: 'PENDING',
      totalAmount: req.body.items.reduce((sum: number, item: any) => sum + item.price * item.quantity, 0),
      items: req.body.items,
    };
    res.status(201).json({
      success: true,
      data: order,
      traceId: req.headers['x-trace-id'],
      tenantId: req.headers['x-tenant-id'],
    } as ApiResponse<Order>);
  });

  app.get('/api/v1/orders/:id', async (req, res) => {
    const order: Order = {
      id: req.params.id,
      orderId: `ORD-${req.params.id}`,
      status: 'CONFIRMED',
      totalAmount: 199.99,
      items: [{ sku: 'SKU-001', quantity: 2, price: 99.99 }],
    };
    res.json({
      success: true,
      data: order,
      traceId: req.headers['x-trace-id'],
      tenantId: req.headers['x-tenant-id'],
    } as ApiResponse<Order>);
  });

  app.get('/api/v1/customers/:id', async (req, res) => {
    const customer: Customer = {
      id: req.params.id,
      name: 'Test Customer',
      email: 'test@example.com',
      tier: 'GOLD',
    };
    res.json({
      success: true,
      data: customer,
      traceId: req.headers['x-trace-id'],
      tenantId: req.headers['x-tenant-id'],
    } as ApiResponse<Customer>);
  });

  app.put('/api/v1/orders/:id/status', async (req, res) => {
    res.json({
      success: true,
      data: { id: req.params.id, status: req.body.status },
      traceId: req.headers['x-trace-id'],
      tenantId: req.headers['x-tenant-id'],
    });
  });

  app.post('/api/v1/products', async (req, res) => {
    const product: Product = {
      id: `prod-${Date.now()}`,
      ...req.body,
      status: 'ACTIVE',
    };
    res.status(201).json({
      success: true,
      data: product,
      traceId: req.headers['x-trace-id'],
      tenantId: req.headers['x-tenant-id'],
    } as ApiResponse<Product>);
  });

  app.use((err: Error, req: express.Request, res: express.Response, next: express.NextFunction) => {
    res.status(500).json({
      success: false,
      error: err.message,
      traceId: req.headers['x-trace-id'],
      tenantId: req.headers['x-tenant-id'],
    });
  });

  return app;
};

describe('API Integration Tests', () => {
  let app: express.Application;

  beforeAll(() => {
    app = createTestApp();
  });

  afterAll(() => {
    vi.clearAllMocks();
  });

  describe('Product API', () => {
    it('should fetch product list successfully', async () => {
      const response = await request(app)
        .get('/api/v1/products')
        .set('x-tenant-id', 'test-tenant')
        .set('x-trace-id', 'trace-product-list')
        .expect('Content-Type', /json/)
        .expect(200);

      expect(response.body.success).toBe(true);
      expect(response.body.data).toBeInstanceOf(Array);
      expect(response.body.data.length).toBeGreaterThan(0);
      expect(response.body.traceId).toBe('trace-product-list');
      expect(response.body.tenantId).toBe('test-tenant');
    });

    it('should fetch single product by ID', async () => {
      const response = await request(app)
        .get('/api/v1/products/1')
        .set('x-tenant-id', 'test-tenant')
        .set('x-trace-id', 'trace-product-detail')
        .expect(200);

      expect(response.body.success).toBe(true);
      expect(response.body.data.id).toBe('1');
      expect(response.body.data.name).toBeDefined();
      expect(response.body.data.sku).toBeDefined();
    });

    it('should create new product', async () => {
      const newProduct = {
        name: 'New Test Product',
        sku: 'SKU-NEW-001',
        price: 199.99,
        stock: 25,
      };

      const response = await request(app)
        .post('/api/v1/products')
        .set('x-tenant-id', 'test-tenant')
        .set('x-trace-id', 'trace-product-create')
        .send(newProduct)
        .expect(201);

      expect(response.body.success).toBe(true);
      expect(response.body.data.name).toBe(newProduct.name);
      expect(response.body.data.sku).toBe(newProduct.sku);
    });
  });

  describe('Order API', () => {
    it('should create order successfully', async () => {
      const orderData = {
        customerId: 'cust-001',
        items: [
          { sku: 'SKU-001', quantity: 2, price: 99.99 },
          { sku: 'SKU-002', quantity: 1, price: 149.99 },
        ],
      };

      const response = await request(app)
        .post('/api/v1/orders')
        .set('x-tenant-id', 'test-tenant')
        .set('x-trace-id', 'trace-order-create')
        .send(orderData)
        .expect(201);

      expect(response.body.success).toBe(true);
      expect(response.body.data.orderId).toBeDefined();
      expect(response.body.data.status).toBe('PENDING');
      expect(response.body.data.totalAmount).toBeCloseTo(349.97, 2);
    });

    it('should fetch order by ID', async () => {
      const response = await request(app)
        .get('/api/v1/orders/order-001')
        .set('x-tenant-id', 'test-tenant')
        .set('x-trace-id', 'trace-order-detail')
        .expect(200);

      expect(response.body.success).toBe(true);
      expect(response.body.data.id).toBe('order-001');
      expect(response.body.data.status).toBe('CONFIRMED');
    });

    it('should update order status', async () => {
      const response = await request(app)
        .put('/api/v1/orders/order-001/status')
        .set('x-tenant-id', 'test-tenant')
        .set('x-trace-id', 'trace-order-status')
        .send({ status: 'SHIPPED' })
        .expect(200);

      expect(response.body.success).toBe(true);
      expect(response.body.data.status).toBe('SHIPPED');
    });
  });

  describe('Customer API', () => {
    it('should fetch customer details', async () => {
      const response = await request(app)
        .get('/api/v1/customers/cust-001')
        .set('x-tenant-id', 'test-tenant')
        .set('x-trace-id', 'trace-customer-detail')
        .expect(200);

      expect(response.body.success).toBe(true);
      expect(response.body.data.id).toBe('cust-001');
      expect(response.body.data.email).toBeDefined();
    });
  });

  describe('Response Format Validation', () => {
    it('should include traceId in all responses', async () => {
      const endpoints = [
        { method: 'get', path: '/api/v1/products' },
        { method: 'get', path: '/api/v1/products/1' },
        { method: 'get', path: '/api/v1/orders/order-001' },
        { method: 'get', path: '/api/v1/customers/cust-001' },
      ];

      for (const endpoint of endpoints) {
        const response = await request(app)
          [endpoint.method](endpoint.path)
          .set('x-tenant-id', 'test-tenant')
          .set('x-trace-id', `trace-${Date.now()}`);

        expect(response.body.traceId).toBeDefined();
        expect(response.body.tenantId).toBe('test-tenant');
      }
    });

    it('should return consistent error format', async () => {
      const response = await request(app)
        .get('/api/v1/nonexistent')
        .set('x-tenant-id', 'test-tenant')
        .set('x-trace-id', 'trace-error');

      expect(response.status).toBe(404);
    });
  });

  describe('Performance Tests', () => {
    it('should handle concurrent requests', async () => {
      const requests = Array(10)
        .fill(null)
        .map((_, i) =>
          request(app)
            .get('/api/v1/products')
            .set('x-tenant-id', 'test-tenant')
            .set('x-trace-id', `trace-concurrent-${i}`)
        );

      const responses = await Promise.all(requests);
      responses.forEach((res) => {
        expect(res.status).toBe(200);
        expect(res.body.success).toBe(true);
      });
    });

    it('should respond within acceptable time', async () => {
      const start = Date.now();
      await request(app)
        .get('/api/v1/products')
        .set('x-tenant-id', 'test-tenant')
        .set('x-trace-id', 'trace-perf');
      const duration = Date.now() - start;

      expect(duration).toBeLessThan(1000);
    });
  });
});

export { createTestApp };
