import dotenv from 'dotenv';
import mongoose, { Mongoose } from 'mongoose';
import IUserCreateDto from '../interfaces/IUserCreateDto';
import IResponse from '../interfaces/IResponse';
import IUserGetDto from '../interfaces/IUserGetDto';
import { User } from '../models/User';
import { EStatuses } from '../enum/EStatuses';
import { generateJWT } from '../helpers/generateJWT';
import IPhotoDto from '../interfaces/IPhotoDto';
import IPhoto from '../interfaces/IPhoto';
import { Photo } from '../models/Photo';

dotenv.config()

export class MongooseDB {
    private client: Mongoose | null = null

    public close = async() => {
        if (!this.client) return
        await this.client.disconnect();
    }

    public init = async (): Promise<void> => {
        try {
            this.client = await mongoose.connect(process.env.MONGO_CLIENT_URL || '')
            console.log('Server connected to MongoDB');
        } catch (err) {
            const error = err as Error;
            console.error('Connected error MongooseDB:', error);
        }
    }

    public createUser = async (userDto: IUserCreateDto): Promise<IResponse<IUserGetDto | null>> => {
        try{
            const user = new User(userDto)
            const data: IUserGetDto = {
                _id: user._id,
                username: user.username, 
                token: generateJWT({_id: user._id, username: user.username})
            }
            const response: IResponse<IUserGetDto> = {
                status: EStatuses.SUCCESS,
                result: data,
                message: 'User created'
            }
            await user.save()
            return response
        } catch(err: unknown){
            const error = err as Error 
            const response: IResponse<null> = {
                status: EStatuses.FAILURE,
                result: null,
                message: error.message
            }
            return response
        }
    }

    public loginUser = async(userDto: IUserCreateDto): Promise<IResponse<IUserGetDto | null>> => {
        try{
            const user = await User.findOne({username: userDto.username})
            if (!user) throw new Error('User not found')
            const isMatch: boolean = await user.checkPassword(userDto.password)
            if (!isMatch) throw new Error('Wrong password')
            const data = {
                _id: user._id,
                username: user.username, 
                token: generateJWT({_id: user._id, username: user.username})
            }
            await user.save()
            const response: IResponse<IUserGetDto> = {
                status: EStatuses.SUCCESS,
                result: data,
                message: 'Access granted'
            }
            return response
        } catch(err: unknown){
            const error = err as Error 
            const response: IResponse<null> = {
                status: EStatuses.FAILURE,
                result: null,
                message: error.message
            }
            return response
        }
    }

    public getAllPhotos = async (): Promise<IResponse<IPhoto[] | null>> => {
        try{
            const data = await Photo.find().populate('user')
            const response: IResponse<IPhoto[]> = {
                status: EStatuses.SUCCESS,
                result: data,
                message: 'Photos found successfully'
            }
            return response
        } catch(err: unknown){
            const error = err as Error 
            const response: IResponse<null> = {
                status: EStatuses.FAILURE,
                result: null,
                message: error.message
            }
            return response
        }
    }

    public addPhoto = async(photoDto: IPhotoDto): Promise<IResponse<IPhoto | null>> => {
        try{
            const user = await User.findOne({_id: photoDto.user})
            if (!user) throw new Error ('User not found!')
            const photo = new Photo(photoDto)
            const data = await photo.save()
            const responseData = await data.populate('user')
            const response: IResponse<IPhoto> = {
                status: EStatuses.SUCCESS,
                result: responseData,
                message: 'Photo added'
            }
            return response
        } catch(err: unknown){
            const error = err as Error 
            const response: IResponse<null> = {
                status: EStatuses.FAILURE,
                result: null,
                message: error.message
            }
            return response
        }
    }

    public deletePhotoById = async(id: string, user: string): Promise<IResponse<IPhoto | null>> => {
        try{
            const photo = await Photo.findOneAndDelete({_id: id, user: user})
            if (!photo) throw new Error('No photo with stated id was found')
            const response: IResponse<IPhoto> = {
                status: EStatuses.SUCCESS,
                result: photo,
                message: 'Photo was deleted successfully'
            }
            return response
        } catch(err: unknown){
            const error = err as Error 
            const response: IResponse<null> = {
                status: EStatuses.FAILURE,
                result: null,
                message: error.message
            }
            return response
        }
    }

    public getPhotosByUserId = async(id: string): Promise<IResponse<IPhoto[] | null>> => {
        try{
            const data = await Photo.find({user: id}).populate('user')
            const response: IResponse<IPhoto[]> = {
                status: EStatuses.SUCCESS,
                result: data,
                message: 'Photos found successfully'
            }
            return response
        } catch(err: unknown){
            const error = err as Error 
            const response: IResponse<null> = {
                status: EStatuses.FAILURE,
                result: null,
                message: error.message
            }
            return response
        }
    }
}

export const mongooseDB = new MongooseDB()