import {
    CanActivate,
    ExecutionContext,
    HttpException,
    HttpStatus,
    Injectable,
    UnauthorizedException,
} from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { AuthService } from './auth.service';
import { ConfigService } from '@nestjs/config';
import { ModelsService } from 'src/models/models.service';
import { Types } from 'mongoose';
import { Reflector } from '@nestjs/core';
import { IS_PUBLIC_KEY } from './public.decorator';

@Injectable()
export class AuthGuard implements CanActivate {
    constructor(
        private jwtservice: JwtService,
        private authService: AuthService,
        private readonly Model: ModelsService,
        private reflector: Reflector
    ) {
    }

    async canActivate(context: ExecutionContext): Promise<boolean> {

        const request = context.switchToHttp().getRequest();

     
        // Check if route is public
        const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [
            context.getHandler(),
            context.getClass(),
        ]);

        // Get token if exists
        let token = request?.headers?.authorization?.split(" ")[1] ||
            request?.query?.authorization;

        // If route is public & token is NOT provided → allow
        if (isPublic && !token) {
            return true;
        }
        if (!token) {
            throw new HttpException("Unauthorized user", HttpStatus.UNAUTHORIZED)
        }
        else {
            try {
                const payload = await this.jwtservice.verifyAsync(token);
                if (payload.scope === 'USER') {
                    // Verify that session exists with this token
                    const sessionData = await this.verifyToken(payload, token);
                    if (sessionData?.length) {
                        let { _id } = payload;
                        let query = { _id: _id }
                        let fetch_user: any = await this.Model.UserModel.findOne(query)
                        if (!fetch_user) throw new HttpException({ message: "Unathorized Scope" }, HttpStatus.UNAUTHORIZED);
                        request.user_data = fetch_user;
                        return request.user_data;
                    }
                    else {
                       throw new HttpException({ message: "Session Expired" }, HttpStatus.UNAUTHORIZED);
                    }
                }
                else {
                    throw new UnauthorizedException("access denied")
                }
            }
            catch (err) {
                throw err
            }
        }
    }

    async verifyToken(payload: any, token: string) {
        const { scope, _id, token_gen_at } = payload;
        let query: any = {
            user_id: new Types.ObjectId(_id),
            access_token: token
        };

        if (scope !== 'USER') {
            throw new HttpException('Invalid scope', HttpStatus.UNAUTHORIZED);
        }

        const projection = { __v: 0 };
        const option = { lean: true };
        const fetch_data: any = await this.Model.SessionModel.find(query, projection, option);

        if (fetch_data.length) {
            return fetch_data;
        } else {
            throw new HttpException('Session not found', HttpStatus.UNAUTHORIZED);
        }
    }


}
@Injectable()
export class ChatAuthGuard {
    constructor(
        private jwtservice: JwtService,
        private readonly Model: ModelsService
    ) { }

    async validateSocket(socket: any): Promise<any> {


        const headers = socket.handshake?.headers || {};
        const token = headers['token'] || headers['authorization'];
        console.log(token, "token------------");

        if (!token) {
            throw new HttpException('Token not received', HttpStatus.UNAUTHORIZED);
        }

        try {
            const secretKey = process.env.SECRET_KEY;
            const payload = await this.jwtservice.verifyAsync(token, { secret: secretKey });
            const { scope } = payload;
            console.log(payload, "payloadiiiiiiiiiiiiii");

            if (scope === 'USER') {
                const sessionData = await this.verifyToken(payload);
                if (sessionData.length) {
                    const { _id } = payload;
                    await this.Model.UserModel.updateOne({ _id }, { socket_id: socket?.id, is_online: true })
                    // socket.user_data = fetch_user;
                    const fetch_user: any = await this.Model.UserModel.findOne({ _id });
                    return fetch_user;
                } else {
                    throw new HttpException('User not found', HttpStatus.UNAUTHORIZED);
                }
            }
            // Optionally handle admin or other scopes
            throw new HttpException('Invalid scope', HttpStatus.UNAUTHORIZED);
        } catch (err) {
            console.error('Socket Auth Error:', err.message);
            throw err;
        }
    }

    async verifyToken(payload: any) {
        const { scope, _id, token_gen_at } = payload;
        let query: any = {
            access_token: { $ne: null },
        };

        if (scope === 'USER') {
            query.user_id = new Types.ObjectId(_id);
        } else {
            throw new HttpException('Invalid scope', HttpStatus.UNAUTHORIZED);
        }

        const projection = { __v: 0 };
        const option = { lean: true };
        const fetch_data: any = await this.Model.SessionModel.find(query, projection, option);

        if (fetch_data.length) {
            return fetch_data;
        } else {
            throw new HttpException('Session not found', HttpStatus.UNAUTHORIZED);
        }
    }
}

