import { Request, Response, NextFunction, Router } from 'express';
import { PartnersService } from '../services/partners.service';
import {
  CreatePartnerDto,
  UpdatePartnerDto,
  CreatePartnerAddressDto,
  CreatePartnerContactDto,
  CreatePartnerBankAccountDto,
} from '../dto';

export class PartnersController {
  public router: Router;

  constructor(private readonly partnersService: PartnersService) {
    this.router = Router();
    this.initializeRoutes();
  }

  private initializeRoutes(): void {
    // Partners
    this.router.get('/', this.findAll.bind(this));
    this.router.get('/customers', this.getCustomers.bind(this));
    this.router.get('/suppliers', this.getSuppliers.bind(this));
    this.router.get('/:id', this.findOne.bind(this));
    this.router.get('/code/:code', this.findByCode.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));

    // Addresses
    this.router.get('/:id/addresses', this.getAddresses.bind(this));
    this.router.post('/:id/addresses', this.createAddress.bind(this));
    this.router.delete('/:id/addresses/:addressId', this.deleteAddress.bind(this));

    // Contacts
    this.router.get('/:id/contacts', this.getContacts.bind(this));
    this.router.post('/:id/contacts', this.createContact.bind(this));
    this.router.delete('/:id/contacts/:contactId', this.deleteContact.bind(this));

    // Bank Accounts
    this.router.get('/:id/bank-accounts', this.getBankAccounts.bind(this));
    this.router.post('/:id/bank-accounts', this.createBankAccount.bind(this));
    this.router.delete('/:id/bank-accounts/:accountId', this.deleteBankAccount.bind(this));
    this.router.post('/:id/bank-accounts/:accountId/verify', this.verifyBankAccount.bind(this));
  }

  // ==================== Partners ====================

  private async findAll(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: 'Tenant ID is required' });
        return;
      }

      const { search, partnerType, category, isActive, salesRepId, limit, offset } = req.query;

      const result = await this.partnersService.findAll({
        tenantId,
        search: search as string,
        partnerType: partnerType as 'customer' | 'supplier' | 'both',
        category: category as string,
        isActive: isActive ? isActive === 'true' : undefined,
        salesRepId: salesRepId as string,
        limit: limit ? parseInt(limit as string, 10) : undefined,
        offset: offset ? parseInt(offset as string, 10) : undefined,
      });

      res.json(result);
    } catch (error) {
      next(error);
    }
  }

  private async findOne(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: 'Tenant ID is required' });
        return;
      }

      const { id } = req.params;
      const partner = await this.partnersService.findOne(id, tenantId);

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

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

  private async findByCode(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: 'Tenant ID is required' });
        return;
      }

      const { code } = req.params;
      const partner = await this.partnersService.findByCode(code, tenantId);

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

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

  private async create(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;

      if (!tenantId) {
        res.status(400).json({ error: 'Tenant ID is required' });
        return;
      }

      const dto: CreatePartnerDto = req.body;
      const partner = await this.partnersService.create(tenantId, dto, userId);
      res.status(201).json({ data: partner });
    } catch (error) {
      next(error);
    }
  }

  private async update(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;

      if (!tenantId) {
        res.status(400).json({ error: 'Tenant ID is required' });
        return;
      }

      const { id } = req.params;
      const dto: UpdatePartnerDto = req.body;
      const partner = await this.partnersService.update(id, tenantId, dto, userId);

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

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

  private async delete(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: 'Tenant ID is required' });
        return;
      }

      const { id } = req.params;
      const deleted = await this.partnersService.delete(id, tenantId);

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

      res.status(204).send();
    } catch (error) {
      next(error);
    }
  }

  private async getCustomers(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: 'Tenant ID is required' });
        return;
      }

      const customers = await this.partnersService.getCustomers(tenantId);
      res.json({ data: customers });
    } catch (error) {
      next(error);
    }
  }

  private async getSuppliers(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: 'Tenant ID is required' });
        return;
      }

      const suppliers = await this.partnersService.getSuppliers(tenantId);
      res.json({ data: suppliers });
    } catch (error) {
      next(error);
    }
  }

  // ==================== Addresses ====================

  private async getAddresses(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const { id } = req.params;
      const addresses = await this.partnersService.getAddresses(id);
      res.json({ data: addresses });
    } catch (error) {
      next(error);
    }
  }

  private async createAddress(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const { id } = req.params;
      const dto: CreatePartnerAddressDto = { ...req.body, partnerId: id };
      const address = await this.partnersService.createAddress(dto);
      res.status(201).json({ data: address });
    } catch (error) {
      next(error);
    }
  }

  private async deleteAddress(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const { addressId } = req.params;
      const deleted = await this.partnersService.deleteAddress(addressId);

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

      res.status(204).send();
    } catch (error) {
      next(error);
    }
  }

  // ==================== Contacts ====================

  private async getContacts(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const { id } = req.params;
      const contacts = await this.partnersService.getContacts(id);
      res.json({ data: contacts });
    } catch (error) {
      next(error);
    }
  }

  private async createContact(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const { id } = req.params;
      const dto: CreatePartnerContactDto = { ...req.body, partnerId: id };
      const contact = await this.partnersService.createContact(dto);
      res.status(201).json({ data: contact });
    } catch (error) {
      next(error);
    }
  }

  private async deleteContact(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const { contactId } = req.params;
      const deleted = await this.partnersService.deleteContact(contactId);

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

      res.status(204).send();
    } catch (error) {
      next(error);
    }
  }

  // ==================== Bank Accounts ====================

  private async getBankAccounts(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const { id } = req.params;
      const bankAccounts = await this.partnersService.getBankAccounts(id);
      res.json({ data: bankAccounts });
    } catch (error) {
      next(error);
    }
  }

  private async createBankAccount(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const { id } = req.params;
      const dto: CreatePartnerBankAccountDto = { ...req.body, partnerId: id };
      const bankAccount = await this.partnersService.createBankAccount(dto);
      res.status(201).json({ data: bankAccount });
    } catch (error) {
      next(error);
    }
  }

  private async deleteBankAccount(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const { accountId } = req.params;
      const deleted = await this.partnersService.deleteBankAccount(accountId);

      if (!deleted) {
        res.status(404).json({ error: 'Bank account not found' });
        return;
      }

      res.status(204).send();
    } catch (error) {
      next(error);
    }
  }

  private async verifyBankAccount(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const { accountId } = req.params;
      const bankAccount = await this.partnersService.verifyBankAccount(accountId);

      if (!bankAccount) {
        res.status(404).json({ error: 'Bank account not found' });
        return;
      }

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