import { Request, Response, NextFunction, Router } from 'express';
import { PersonsService } from '../services/persons.service';
import { CreatePersonDto, UpdatePersonDto } from '../dto';

export class PersonsController {
  public router: Router;

  constructor(private readonly personsService: PersonsService) {
    this.router = Router();
    this.initializeRoutes();
  }

  private initializeRoutes(): void {
    this.router.get('/', this.findAll.bind(this));
    this.router.get('/responsible', this.getResponsiblePersons.bind(this));
    this.router.get('/:id', this.findOne.bind(this));
    this.router.get('/email/:email', this.findByEmail.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/verify', this.verify.bind(this));
    this.router.post('/:id/unverify', this.unverify.bind(this));
    this.router.post('/:id/set-responsible', this.setAsResponsible.bind(this));
  }

  private async findAll(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const { search, email, isVerified, isResponsibleForTenant, limit, offset } = req.query;

      const result = await this.personsService.findAll({
        search: search as string,
        email: email as string,
        isVerified: isVerified ? isVerified === 'true' : undefined,
        isResponsibleForTenant: isResponsibleForTenant ? isResponsibleForTenant === 'true' : undefined,
        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 { id } = req.params;
      const person = await this.personsService.findOne(id);

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

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

  private async findByEmail(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const { email } = req.params;
      const person = await this.personsService.findByEmail(email);

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

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

  private async create(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const dto: CreatePersonDto = req.body;
      const person = await this.personsService.create(dto);
      res.status(201).json({ data: person });
    } catch (error) {
      next(error);
    }
  }

  private async update(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const { id } = req.params;
      const dto: UpdatePersonDto = req.body;
      const person = await this.personsService.update(id, dto);

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

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

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

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

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

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

      const person = await this.personsService.verify(id, verifiedBy);

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

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

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

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

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

  private async setAsResponsible(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const { id } = req.params;
      const { isResponsible } = req.body;

      const person = await this.personsService.setAsResponsible(id, isResponsible ?? true);

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

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

  private async getResponsiblePersons(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const persons = await this.personsService.getResponsiblePersons();
      res.json({ data: persons });
    } catch (error) {
      next(error);
    }
  }
}
