import { Request, Response } from 'express';
import CustomerDatabaseService from '../services/db/customerList';
import PasswordMgmt from '../utils/security/passwordMgmt';
import { Role } from '../declarations/roles';
import {
    Customer,
    CustomerDbInsert,
    Id,
    CustomerUpdateInput,
    CustomerCreationInput,
} from '../declarations/validations';
import SecurityUtil from '../utils/security/securityUtil';
import INotificationService from '../declarations/notificationServiceInterface';
import { nodemailerMailController } from './mailController';

// TODO fulfill DRY principle

export class CustomerController {
    public customerListService = new CustomerDatabaseService();

    public notificationService: INotificationService = nodemailerMailController;

    async createCustomer(req: Request, res: Response) {
        req.body.zip = parseInt(req.body.zip, 10);

        const result = await CustomerCreationInput.safeParseAsync({
            username: req.body.mail,
            ...req.body,
        });

        if (!result.success) {
            res.status(400).send({ error: 'Invalid input' });
            return;
        }

        const password = PasswordMgmt.generatePassword(10);
        const passwordHash = await PasswordMgmt.generatePasswordHash(password);

        const customer = CustomerDbInsert.parse({
            passwordHash,
            ...result.data,
        });

        this.customerListService.insertCustomer(customer).then(() => {
            this.notificationService.sendWelcomeCustomerNotification({
                user: {
                    username: customer.username,
                    password,
                    mail: customer.mail,
                    profile: Customer.parse(customer),
                },
            });

            res.send({
                success: `Customer ${customer.firstname} ${customer.lastname} successfully created`,
                initialPassword: password,
                username: customer.username,
            });
        });
    }

    getCustomer(req: Request, res: Response) {
        const idInput = parseInt(req.params.id, 10);

        const result = Id.safeParse(idInput);

        if (!result.success) {
            res.status(400).send({ error: 'Invalid identifier' });
            return;
        }

        const id = result.data;
        const currentUser = SecurityUtil.currentUser(req);
        switch (currentUser?.role) {
            case Role.Customer:
                if (id !== currentUser?.profileId) {
                    res.status(403).send({ error: 'Not yourself' });
                    return;
                }
                this.customerListService
                    .findCustomerById(id)
                    .then((customer) => {
                        res.send({ customer });
                    });

                break;
            case Role.Employee:
                this.customerListService
                    .findCustomerById(id)
                    .then((customer) => {
                        res.send({ customer });
                    });
                break;

            default:
                res.sendStatus(403);
        }
    }

    updateCustomer(req: Request, res: Response) {
        const idInput = parseInt(req.params.id, 10);

        if (req.body.zip) {
            req.body.zip = parseInt(req.body.zip, 10);
        }

        const result = CustomerUpdateInput.safeParse({
            id: idInput,
            ...req.body,
        });

        if (!result.success) {
            res.status(400).send({ error: 'Invalid input' });
            return;
        }

        const customer = {
            ...result.data,
        };

        const currentUser = SecurityUtil.currentUser(req);
        switch (currentUser?.role) {
            case Role.Customer:
                if (customer.id !== currentUser.profileId) {
                    res.status(403).send({ error: 'Not yourself' });
                    return;
                }
                this.customerListService
                    .updateCustomer(customer)
                    .then((updatedCustomer) => res.send({ updatedCustomer }));

                break;
            case Role.Employee:
                this.customerListService
                    .updateCustomer(customer)
                    .then((updatedCustomer) => res.send({ updatedCustomer }));
                break;

            default:
                res.sendStatus(403);
        }
    }

    getCustomers(req: Request, res: Response) {
        this.customerListService.findAllCustomers().then((customers) => {
            res.send({ success: 'Customers successfully fetched.', customers });
        });
    }
}

export const customerController = new CustomerController();
