import { Request, Response, NextFunction, Router } from 'express';
import { InvoicesService } from '../services';

export class InvoicesController {
  public router: Router;
  constructor(private readonly invoicesService: InvoicesService) {
    this.router = Router();
    this.router.get('/', this.findAll.bind(this));
    this.router.get('/:id', this.findOne.bind(this));
    this.router.post('/', this.create.bind(this));
    this.router.patch('/:id', this.update.bind(this));
    this.router.delete('/:id', this.delete.bind(this));
    this.router.post('/:id/validate', this.validate.bind(this));
  }

  private async findAll(req: Request, res: Response, next: NextFunction) {
    try {
      const tenantId = req.headers['x-tenant-id'] as string;
      if (!tenantId) { res.status(400).json({ error: 'Tenant ID required' }); return; }
      const { invoiceType, partnerId, status, limit, offset } = req.query;
      const result = await this.invoicesService.findAllInvoices({ tenantId, invoiceType: invoiceType as string, partnerId: partnerId as string, status: status as string, limit: limit ? parseInt(limit as string) : undefined, offset: offset ? parseInt(offset as string) : undefined });
      res.json(result);
    } catch (e) { next(e); }
  }

  private async findOne(req: Request, res: Response, next: NextFunction) {
    try {
      const tenantId = req.headers['x-tenant-id'] as string;
      if (!tenantId) { res.status(400).json({ error: 'Tenant ID required' }); return; }
      const invoice = await this.invoicesService.findInvoice(req.params.id, tenantId);
      if (!invoice) { res.status(404).json({ error: 'Not found' }); return; }
      res.json({ data: invoice });
    } catch (e) { next(e); }
  }

  private async create(req: Request, res: Response, next: NextFunction) {
    try {
      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: 'Tenant ID required' }); return; }
      const invoice = await this.invoicesService.createInvoice(tenantId, req.body, userId);
      res.status(201).json({ data: invoice });
    } catch (e) { next(e); }
  }

  private async update(req: Request, res: Response, next: NextFunction) {
    try {
      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: 'Tenant ID required' }); return; }
      const invoice = await this.invoicesService.updateInvoice(req.params.id, tenantId, req.body, userId);
      if (!invoice) { res.status(404).json({ error: 'Not found' }); return; }
      res.json({ data: invoice });
    } catch (e) { next(e); }
  }

  private async delete(req: Request, res: Response, next: NextFunction) {
    try {
      const tenantId = req.headers['x-tenant-id'] as string;
      if (!tenantId) { res.status(400).json({ error: 'Tenant ID required' }); return; }
      const deleted = await this.invoicesService.deleteInvoice(req.params.id, tenantId);
      if (!deleted) { res.status(404).json({ error: 'Not found' }); return; }
      res.status(204).send();
    } catch (e) { next(e); }
  }

  private async validate(req: Request, res: Response, next: NextFunction) {
    try {
      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: 'Tenant ID required' }); return; }
      const invoice = await this.invoicesService.validateInvoice(req.params.id, tenantId, userId);
      if (!invoice) { res.status(400).json({ error: 'Cannot validate' }); return; }
      res.json({ data: invoice });
    } catch (e) { next(e); }
  }
}

export class PaymentsController {
  public router: Router;
  constructor(private readonly invoicesService: InvoicesService) {
    this.router = Router();
    this.router.get('/', this.findAll.bind(this));
    this.router.get('/:id', this.findOne.bind(this));
    this.router.post('/', this.create.bind(this));
    this.router.post('/:id/confirm', this.confirm.bind(this));
  }

  private async findAll(req: Request, res: Response, next: NextFunction) {
    try {
      const tenantId = req.headers['x-tenant-id'] as string;
      if (!tenantId) { res.status(400).json({ error: 'Tenant ID required' }); return; }
      const { partnerId, limit, offset } = req.query;
      const result = await this.invoicesService.findAllPayments(tenantId, partnerId as string, limit ? parseInt(limit as string) : undefined, offset ? parseInt(offset as string) : undefined);
      res.json(result);
    } catch (e) { next(e); }
  }

  private async findOne(req: Request, res: Response, next: NextFunction) {
    try {
      const tenantId = req.headers['x-tenant-id'] as string;
      if (!tenantId) { res.status(400).json({ error: 'Tenant ID required' }); return; }
      const payment = await this.invoicesService.findPayment(req.params.id, tenantId);
      if (!payment) { res.status(404).json({ error: 'Not found' }); return; }
      res.json({ data: payment });
    } catch (e) { next(e); }
  }

  private async create(req: Request, res: Response, next: NextFunction) {
    try {
      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: 'Tenant ID required' }); return; }
      const payment = await this.invoicesService.createPayment(tenantId, req.body, userId);
      res.status(201).json({ data: payment });
    } catch (e) { next(e); }
  }

  private async confirm(req: Request, res: Response, next: NextFunction) {
    try {
      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: 'Tenant ID required' }); return; }
      const payment = await this.invoicesService.confirmPayment(req.params.id, tenantId, userId);
      if (!payment) { res.status(400).json({ error: 'Cannot confirm' }); return; }
      res.json({ data: payment });
    } catch (e) { next(e); }
  }
}
