import { Injectable, Logger } from "@nestjs/common";
import { Request, response, Response } from "express";
import ResponseDto from "../../dto/response.dto";
import { UserDtoCreate, UserDtoLogIn } from "../../dto/user.dto";
import UserEntity from "../../entity/user.entity";
import { EntityManager } from "typeorm";
import * as bcrypt from 'bcrypt';
import * as jwt from 'jsonwebtoken';

@Injectable()
export default class UserProvider {

    private readonly logger = new Logger(UserProvider.name);
    
    constructor(private readonly entityManager: EntityManager){}

    /**
     * LogIn event
     * @param response 
     * @param payload 
     */
    async logIn(request: Request, response: Response, payload: UserDtoLogIn){

        let isLoggedIn  = false;
        let data: any   = null;

        try{

            const user =
                await this.entityManager.findOneOrFail(
                    UserEntity,
                    {
                        where: {
                            username: payload.username
                        }
                    }
                );
            if(!user){
                throw new Error("user_not_found");
            }

            const isValidPassword = await bcrypt.compare(payload.password!, user.password!);
            if(!isValidPassword){
                throw new Error("user_not_found");
            }

            // Generate JSON web token
            const token = 
                jwt.sign(
                    {
                        id  : user.id,
                        name: user.title
                    },
                    process.env.JWT as string
                );

            response.cookie(
                'session', 
                token, 
                {
                    sameSite: "none",
                    httpOnly: true,
                    maxAge  : 1000 * 60 * 60, // 1h
                    secure  : true
                }
            );
            
            data = {
                "id"        : user.id,
                "code"      : user.code,
                "title"     : user.title,
                "permission":
                    {
                        "pArticle"          : user.pArticle,
                        "pAttribute"        : user.pAttribute,
                        "pCategory"         : user.pCategory,
                        "pCreditNoteCounter": user.pCreditNoteCounter,
                        "pCustomer"         : user.pCustomer,
                        "pDevice"           : user.pDevice,
                        "pDocument"         : user.pDocument,
                        "pEvent"            : user.pEvent,
                        "pInvoiceCounter"   : user.pInvoiceCounter,
                        "pMovement"         : user.pMovement,
                        "pOperator"         : user.pOperator,
                        "pPayment"          : user.pPayment,
                        "pPrice"            : user.pPrice,
                        "pRate"             : user.pRate,
                        "pStorage"          : user.pStorage,
                        "pStore"            : user.pStore,
                        "pSupplier"         : user.pSupplier,
                        "pTipology"         : user.pTipology,
                        "pUnitMeasure"      : user.pUnitMeasure,
                        "pUser"             : user.pUser,
                        "pMedia"            : user.pMedia,
                        "pAi"               : user.pAi
                    }
            };
            isLoggedIn  = true;

        }catch(e: any){
            this.logger.error(e.message);
        }

        response.json(
            { "success": isLoggedIn, "data": data }
        );
    }

    /**
     * Create user
     * @param request 
     * @param payload 
     */
    async create(request: Request, payload: UserDtoCreate): Promise<ResponseDto>{

        const response: ResponseDto = new ResponseDto();

        try{

            const exists = 
                await this.entityManager.exists(
                    UserEntity,
                    {
                        where: {
                            username: payload.username
                        }
                    }
                );
            if(exists){
                throw new Error("username_exists");
            }

            const encPassword = await bcrypt.hash(payload.password!, 10);

            const user: UserEntity  = new UserEntity();
            user.code               = payload.code;
            user.title              = payload.title;
            user.password           = encPassword;
            user.username           = payload.username;
            user.pArticle           = payload.pArticle;
            user.pAttribute         = payload.pAttribute;
            user.pCategory          = payload.pCategory;
            user.pCreditNoteCounter = payload.pCreditNoteCounter;
            user.pCustomer          = payload.pCustomer;
            user.pDevice            = payload.pDevice;
            user.pDocument          = payload.pDocument;
            user.pEvent             = payload.pEvent;
            user.pInvoiceCounter    = payload.pInvoiceCounter;
            user.pMovement          = payload.pMovement;
            user.pOperator          = payload.pOperator;
            user.pPayment           = payload.pPayment;
            user.pPrice             = payload.pPrice;
            user.pRate              = payload.pRate;
            user.pStorage           = payload.pStorage;
            user.pStore             = payload.pStore;
            user.pSupplier          = payload.pSupplier;
            user.pTipology          = payload.pTipology;
            user.pUnitMeasure       = payload.pUnitMeasure;
            user.pUser              = payload.pUser;
            user.pMedia             = payload.pMedia;
            user.pAi                = payload.pAi;

            await this.entityManager.save(user);
            response.success = true;

        }catch(e: any){
            response.error = e.message;
            this.logger.error(e.message);
        }

        return response;
    }

    /**
     * Clean session
     * @param response 
     */
    async logOut(response: Response){
        response.clearCookie('session');
        response.json({success: true});
    }
    
    /**
     * Check if current session is still active
     * @param response 
     */
    async isSessionActive(request: Request){

        const response: ResponseDto = new ResponseDto();

        try{

            const idUser = request['id'];
            const user =
                await this.entityManager.findOneOrFail(
                    UserEntity,
                    {
                        where: {
                            id: idUser
                        }
                    }
                );

            response.success = true;
            response.data    = {
                "id"        : user.id,
                "code"      : user.code,
                "title"     : user.title,
                "swVersion" : process.env.SW_VERSION,
                "permission":
                    {
                        "pArticle"          : user.pArticle,
                        "pAttribute"        : user.pAttribute,
                        "pCategory"         : user.pCategory,
                        "pCreditNoteCounter": user.pCreditNoteCounter,
                        "pCustomer"         : user.pCustomer,
                        "pDevice"           : user.pDevice,
                        "pDocument"         : user.pDocument,
                        "pEvent"            : user.pEvent,
                        "pInvoiceCounter"   : user.pInvoiceCounter,
                        "pMovement"         : user.pMovement,
                        "pOperator"         : user.pOperator,
                        "pPayment"          : user.pPayment,
                        "pPrice"            : user.pPrice,
                        "pRate"             : user.pRate,
                        "pStorage"          : user.pStorage,
                        "pStore"            : user.pStore,
                        "pSupplier"         : user.pSupplier,
                        "pTipology"         : user.pTipology,
                        "pUnitMeasure"      : user.pUnitMeasure,
                        "pUser"             : user.pUser,
                        "pMedia"            : user.pMedia,
                        "pAi"               : user.pAi
                    }
            };

        }catch(e: any){
            response.error = e.message;
            this.logger.error(e.message);
        }

        return response;
    }

}