import { Request, Response, NextFunction, Router } from 'express';
import { AIService, ConversationFilters } from '../services/ai.service';

export class AIController {
  public router: Router;

  constructor(private readonly aiService: AIService) {
    this.router = Router();
    this.initializeRoutes();
  }

  private initializeRoutes(): void {
    // Models
    this.router.get('/models', this.findAllModels.bind(this));
    this.router.get('/models/:id', this.findModel.bind(this));
    this.router.get('/models/code/:code', this.findModelByCode.bind(this));
    this.router.get('/models/provider/:provider', this.findModelsByProvider.bind(this));
    this.router.get('/models/type/:type', this.findModelsByType.bind(this));

    // Prompts
    this.router.get('/prompts', this.findAllPrompts.bind(this));
    this.router.get('/prompts/:id', this.findPrompt.bind(this));
    this.router.get('/prompts/code/:code', this.findPromptByCode.bind(this));
    this.router.post('/prompts', this.createPrompt.bind(this));
    this.router.patch('/prompts/:id', this.updatePrompt.bind(this));

    // Conversations
    this.router.get('/conversations', this.findConversations.bind(this));
    this.router.get('/conversations/user/:userId', this.findUserConversations.bind(this));
    this.router.get('/conversations/:id', this.findConversation.bind(this));
    this.router.post('/conversations', this.createConversation.bind(this));
    this.router.patch('/conversations/:id', this.updateConversation.bind(this));
    this.router.post('/conversations/:id/archive', this.archiveConversation.bind(this));

    // Messages
    this.router.get('/conversations/:conversationId/messages', this.findMessages.bind(this));
    this.router.post('/conversations/:conversationId/messages', this.addMessage.bind(this));
    this.router.get('/conversations/:conversationId/tokens', this.getConversationTokenCount.bind(this));

    // Usage & Quotas
    this.router.post('/usage', this.logUsage.bind(this));
    this.router.get('/usage/stats', this.getUsageStats.bind(this));
    this.router.get('/quotas', this.getTenantQuota.bind(this));
    this.router.patch('/quotas', this.updateTenantQuota.bind(this));
    this.router.get('/quotas/check', this.checkQuotaAvailable.bind(this));
  }

  // ============================================
  // MODELS
  // ============================================

  private async findAllModels(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const models = await this.aiService.findAllModels();
      res.json({ data: models, total: models.length });
    } catch (error) {
      next(error);
    }
  }

  private async findModel(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const { id } = req.params;
      const model = await this.aiService.findModel(id);

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

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

  private async findModelByCode(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const { code } = req.params;
      const model = await this.aiService.findModelByCode(code);

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

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

  private async findModelsByProvider(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const { provider } = req.params;
      const models = await this.aiService.findModelsByProvider(provider);
      res.json({ data: models, total: models.length });
    } catch (error) {
      next(error);
    }
  }

  private async findModelsByType(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const { type } = req.params;
      const models = await this.aiService.findModelsByType(type);
      res.json({ data: models, total: models.length });
    } catch (error) {
      next(error);
    }
  }

  // ============================================
  // PROMPTS
  // ============================================

  private async findAllPrompts(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const tenantId = req.headers['x-tenant-id'] as string;
      const prompts = await this.aiService.findAllPrompts(tenantId);
      res.json({ data: prompts, total: prompts.length });
    } catch (error) {
      next(error);
    }
  }

  private async findPrompt(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const { id } = req.params;
      const prompt = await this.aiService.findPrompt(id);

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

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

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

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

      // Increment usage count
      await this.aiService.incrementPromptUsage(prompt.id);

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

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

      const prompt = await this.aiService.createPrompt(tenantId, req.body, userId);
      res.status(201).json({ data: prompt });
    } catch (error) {
      next(error);
    }
  }

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

      const prompt = await this.aiService.updatePrompt(id, req.body, userId);

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

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

  // ============================================
  // CONVERSATIONS
  // ============================================

  private async findConversations(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const tenantId = req.headers['x-tenant-id'] as string;
      const filters: ConversationFilters = {
        userId: req.query.userId as string,
        modelId: req.query.modelId as string,
        status: req.query.status as string,
      };

      if (req.query.startDate) filters.startDate = new Date(req.query.startDate as string);
      if (req.query.endDate) filters.endDate = new Date(req.query.endDate as string);

      const limit = parseInt(req.query.limit as string) || 50;

      const conversations = await this.aiService.findConversations(tenantId, filters, limit);
      res.json({ data: conversations, total: conversations.length });
    } catch (error) {
      next(error);
    }
  }

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

      const conversations = await this.aiService.findUserConversations(tenantId, userId, limit);
      res.json({ data: conversations, total: conversations.length });
    } catch (error) {
      next(error);
    }
  }

  private async findConversation(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const { id } = req.params;
      const conversation = await this.aiService.findConversation(id);

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

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

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

      const conversation = await this.aiService.createConversation(tenantId, userId, req.body);
      res.status(201).json({ data: conversation });
    } catch (error) {
      next(error);
    }
  }

  private async updateConversation(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const { id } = req.params;
      const conversation = await this.aiService.updateConversation(id, req.body);

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

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

  private async archiveConversation(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const { id } = req.params;
      const archived = await this.aiService.archiveConversation(id);

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

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

  // ============================================
  // MESSAGES
  // ============================================

  private async findMessages(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const { conversationId } = req.params;
      const messages = await this.aiService.findMessages(conversationId);
      res.json({ data: messages, total: messages.length });
    } catch (error) {
      next(error);
    }
  }

  private async addMessage(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const { conversationId } = req.params;
      const message = await this.aiService.addMessage(conversationId, req.body);
      res.status(201).json({ data: message });
    } catch (error) {
      next(error);
    }
  }

  private async getConversationTokenCount(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const { conversationId } = req.params;
      const tokenCount = await this.aiService.getConversationTokenCount(conversationId);
      res.json({ data: { tokenCount } });
    } catch (error) {
      next(error);
    }
  }

  // ============================================
  // USAGE & QUOTAS
  // ============================================

  private async logUsage(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const tenantId = req.headers['x-tenant-id'] as string;
      const log = await this.aiService.logUsage(tenantId, req.body);
      res.status(201).json({ data: log });
    } catch (error) {
      next(error);
    }
  }

  private async getUsageStats(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const tenantId = req.headers['x-tenant-id'] as string;
      const startDate = new Date(req.query.startDate as string || Date.now() - 30 * 24 * 60 * 60 * 1000);
      const endDate = new Date(req.query.endDate as string || Date.now());

      const stats = await this.aiService.getUsageStats(tenantId, startDate, endDate);
      res.json({ data: stats });
    } catch (error) {
      next(error);
    }
  }

  private async getTenantQuota(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const tenantId = req.headers['x-tenant-id'] as string;
      const quota = await this.aiService.getTenantQuota(tenantId);
      res.json({ data: quota });
    } catch (error) {
      next(error);
    }
  }

  private async updateTenantQuota(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const tenantId = req.headers['x-tenant-id'] as string;
      const quota = await this.aiService.updateTenantQuota(tenantId, req.body);
      res.json({ data: quota });
    } catch (error) {
      next(error);
    }
  }

  private async checkQuotaAvailable(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const tenantId = req.headers['x-tenant-id'] as string;
      const result = await this.aiService.checkQuotaAvailable(tenantId);
      res.json({ data: result });
    } catch (error) {
      next(error);
    }
  }
}
