import { Request, Response, NextFunction, Router } from 'express';
import { McpServerService } from '../services/mcp-server.service';
import { McpContext, CallerType } from '../interfaces';

export class McpController {
  public router: Router;

  constructor(private readonly mcpService: McpServerService) {
    this.router = Router();
    this.initializeRoutes();
  }

  private initializeRoutes(): void {
    // Tools
    this.router.get('/tools', this.listTools.bind(this));
    this.router.get('/tools/:name', this.getTool.bind(this));
    this.router.post('/tools/call', this.callTool.bind(this));

    // Resources
    this.router.get('/resources', this.listResources.bind(this));
    this.router.get('/resources/*', this.getResource.bind(this));

    // History / Audit
    this.router.get('/tool-calls', this.getToolCallHistory.bind(this));
    this.router.get('/tool-calls/:id', this.getToolCallDetails.bind(this));
    this.router.get('/stats', this.getToolStats.bind(this));
  }

  // ============================================
  // TOOLS
  // ============================================

  private async listTools(_req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const tools = this.mcpService.listTools();
      res.json({ data: tools, total: tools.length });
    } catch (error) {
      next(error);
    }
  }

  private async getTool(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const { name } = req.params;
      const tool = this.mcpService.getTool(name);

      if (!tool) {
        res.status(404).json({ error: 'Tool not found' });
        return;
      }

      res.json({ data: tool });
    } catch (error) {
      next(error);
    }
  }

  private async callTool(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const { tool, parameters } = req.body;
      const tenantId = req.headers['x-tenant-id'] as string;
      const userId = req.headers['x-user-id'] as string;
      const agentId = req.headers['x-agent-id'] as string;
      const conversationId = req.headers['x-conversation-id'] as string;

      if (!tool) {
        res.status(400).json({ error: 'tool name is required' });
        return;
      }

      if (!tenantId) {
        res.status(400).json({ error: 'x-tenant-id header is required' });
        return;
      }

      const context: McpContext = {
        tenantId,
        userId,
        agentId,
        conversationId,
        callerType: (req.headers['x-caller-type'] as CallerType) || 'api',
        permissions: this.extractPermissions(req),
      };

      const result = await this.mcpService.callTool(tool, parameters || {}, context);
      res.json({ data: result });
    } catch (error) {
      next(error);
    }
  }

  // ============================================
  // RESOURCES
  // ============================================

  private async listResources(_req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const resources = this.mcpService.listResources();
      res.json({ data: resources, total: resources.length });
    } catch (error) {
      next(error);
    }
  }

  private async getResource(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const uri = 'erp://' + req.params[0];
      const tenantId = req.headers['x-tenant-id'] as string;
      const userId = req.headers['x-user-id'] as string;

      if (!tenantId) {
        res.status(400).json({ error: 'x-tenant-id header is required' });
        return;
      }

      const context: McpContext = {
        tenantId,
        userId,
        callerType: 'api',
        permissions: this.extractPermissions(req),
      };

      const content = await this.mcpService.getResource(uri, context);
      res.json({ data: { uri, content } });
    } catch (error: any) {
      if (error.message.includes('not found')) {
        res.status(404).json({ error: error.message });
        return;
      }
      next(error);
    }
  }

  // ============================================
  // HISTORY / AUDIT
  // ============================================

  private async getToolCallHistory(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const tenantId = req.headers['x-tenant-id'] as string;

      if (!tenantId) {
        res.status(400).json({ error: 'x-tenant-id header is required' });
        return;
      }

      const filters = {
        toolName: req.query.toolName as string,
        status: req.query.status as any,
        startDate: req.query.startDate ? new Date(req.query.startDate as string) : undefined,
        endDate: req.query.endDate ? new Date(req.query.endDate as string) : undefined,
        page: parseInt(req.query.page as string) || 1,
        limit: Math.min(parseInt(req.query.limit as string) || 20, 100),
      };

      const result = await this.mcpService.getCallHistory(tenantId, filters);
      res.json(result);
    } catch (error) {
      next(error);
    }
  }

  private async getToolCallDetails(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const { id } = req.params;
      const tenantId = req.headers['x-tenant-id'] as string;

      if (!tenantId) {
        res.status(400).json({ error: 'x-tenant-id header is required' });
        return;
      }

      const call = await this.mcpService.getCallDetails(id, tenantId);

      if (!call) {
        res.status(404).json({ error: 'Tool call not found' });
        return;
      }

      res.json({ data: call });
    } catch (error) {
      next(error);
    }
  }

  private async getToolStats(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const tenantId = req.headers['x-tenant-id'] as string;

      if (!tenantId) {
        res.status(400).json({ error: 'x-tenant-id header is required' });
        return;
      }

      const startDate = req.query.startDate
        ? new Date(req.query.startDate as string)
        : new Date(Date.now() - 7 * 24 * 60 * 60 * 1000); // 7 days ago
      const endDate = req.query.endDate
        ? new Date(req.query.endDate as string)
        : new Date();

      const stats = await this.mcpService.getToolStats(tenantId, startDate, endDate);
      res.json({ data: stats, total: stats.length });
    } catch (error) {
      next(error);
    }
  }

  // ============================================
  // HELPERS
  // ============================================

  private extractPermissions(req: Request): string[] {
    const permHeader = req.headers['x-permissions'] as string;
    if (!permHeader) return [];

    try {
      return JSON.parse(permHeader);
    } catch {
      return permHeader.split(',').map((p) => p.trim());
    }
  }
}
