/**
 * Invoices Controller
 *
 * REST API endpoints for invoice management
 */

import { Router, Request, Response, NextFunction } from 'express';
import { DataSource } from 'typeorm';
import { InvoicesService } from '../services';
import {
  CreateInvoiceDto,
  UpdateInvoiceDto,
  RecordPaymentDto,
  VoidInvoiceDto,
  RefundInvoiceDto,
  GenerateInvoiceDto,
  InvoiceFilterDto,
} from '../dto';

export class InvoicesController {
  public router: Router;
  private service: InvoicesService;

  constructor(dataSource: DataSource) {
    this.router = Router();
    this.service = new InvoicesService(dataSource);
    this.initializeRoutes();
  }

  private initializeRoutes(): void {
    // Stats
    this.router.get('/stats', this.getStats.bind(this));

    // List and search
    this.router.get('/', this.getAll.bind(this));
    this.router.get('/tenant/:tenantId', this.getByTenant.bind(this));
    this.router.get('/:id', this.getById.bind(this));
    this.router.get('/number/:invoiceNumber', this.getByNumber.bind(this));

    // Create
    this.router.post('/', this.create.bind(this));
    this.router.post('/generate', this.generate.bind(this));

    // Update
    this.router.put('/:id', this.update.bind(this));

    // Actions
    this.router.post('/:id/send', this.send.bind(this));
    this.router.post('/:id/payment', this.recordPayment.bind(this));
    this.router.post('/:id/void', this.void.bind(this));
    this.router.post('/:id/refund', this.refund.bind(this));

    // Batch operations
    this.router.post('/mark-overdue', this.markOverdue.bind(this));
  }

  /**
   * GET /invoices/stats
   * Get invoice statistics
   */
  private async getStats(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const { tenantId } = req.query;
      const stats = await this.service.getStats(tenantId as string);
      res.json({ data: stats });
    } catch (error) {
      next(error);
    }
  }

  /**
   * GET /invoices
   * Get all invoices with filters
   */
  private async getAll(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const filter: InvoiceFilterDto = {
        tenantId: req.query.tenantId as string,
        status: req.query.status as any,
        dateFrom: req.query.dateFrom ? new Date(req.query.dateFrom as string) : undefined,
        dateTo: req.query.dateTo ? new Date(req.query.dateTo as string) : undefined,
        overdue: req.query.overdue === 'true',
        limit: req.query.limit ? parseInt(req.query.limit as string) : undefined,
        offset: req.query.offset ? parseInt(req.query.offset as string) : undefined,
      };

      const result = await this.service.findAll(filter);
      res.json(result);
    } catch (error) {
      next(error);
    }
  }

  /**
   * GET /invoices/tenant/:tenantId
   * Get invoices for specific tenant
   */
  private async getByTenant(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const result = await this.service.findAll({
        tenantId: req.params.tenantId,
        limit: req.query.limit ? parseInt(req.query.limit as string) : 50,
        offset: req.query.offset ? parseInt(req.query.offset as string) : 0,
      });
      res.json(result);
    } catch (error) {
      next(error);
    }
  }

  /**
   * GET /invoices/:id
   * Get invoice by ID
   */
  private async getById(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const invoice = await this.service.findById(req.params.id);

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

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

  /**
   * GET /invoices/number/:invoiceNumber
   * Get invoice by number
   */
  private async getByNumber(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const invoice = await this.service.findByNumber(req.params.invoiceNumber);

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

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

  /**
   * POST /invoices
   * Create invoice manually
   */
  private async create(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const dto: CreateInvoiceDto = req.body;
      const invoice = await this.service.create(dto);
      res.status(201).json({ data: invoice });
    } catch (error) {
      next(error);
    }
  }

  /**
   * POST /invoices/generate
   * Generate invoice from subscription
   */
  private async generate(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const dto: GenerateInvoiceDto = req.body;
      const invoice = await this.service.generateFromSubscription(dto);
      res.status(201).json({ data: invoice });
    } catch (error) {
      next(error);
    }
  }

  /**
   * PUT /invoices/:id
   * Update invoice
   */
  private async update(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const dto: UpdateInvoiceDto = req.body;
      const invoice = await this.service.update(req.params.id, dto);
      res.json({ data: invoice });
    } catch (error) {
      next(error);
    }
  }

  /**
   * POST /invoices/:id/send
   * Send invoice to customer
   */
  private async send(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const invoice = await this.service.send(req.params.id);
      res.json({ data: invoice });
    } catch (error) {
      next(error);
    }
  }

  /**
   * POST /invoices/:id/payment
   * Record payment on invoice
   */
  private async recordPayment(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const dto: RecordPaymentDto = req.body;
      const invoice = await this.service.recordPayment(req.params.id, dto);
      res.json({ data: invoice });
    } catch (error) {
      next(error);
    }
  }

  /**
   * POST /invoices/:id/void
   * Void an invoice
   */
  private async void(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const dto: VoidInvoiceDto = req.body;
      const invoice = await this.service.void(req.params.id, dto);
      res.json({ data: invoice });
    } catch (error) {
      next(error);
    }
  }

  /**
   * POST /invoices/:id/refund
   * Refund an invoice
   */
  private async refund(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const dto: RefundInvoiceDto = req.body;
      const invoice = await this.service.refund(req.params.id, dto);
      res.json({ data: invoice });
    } catch (error) {
      next(error);
    }
  }

  /**
   * POST /invoices/mark-overdue
   * Mark all overdue invoices (scheduled job endpoint)
   */
  private async markOverdue(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const count = await this.service.markOverdueInvoices();
      res.json({ data: { markedOverdue: count } });
    } catch (error) {
      next(error);
    }
  }
}
