import { Injectable, Logger } from '@nestjs/common';
import { HttpService } from '@nestjs/axios';
import { firstValueFrom } from 'rxjs';
import * as xmldom from 'xmldom';
import * as qs from 'qs';
import { HashService } from './hash.service';
import { SettingsService } from './settings.service';
import { TelegramService, ChatId } from './telegram.service';
import { Cron, CronExpression } from '@nestjs/schedule';
import { HttpsProxyAgent } from 'https-proxy-agent';
import { ProxyService } from './proxy.service';

export interface TokenResObj {
    status: boolean; 
    token: string | null
}

@Injectable()
export class TokenService {
    private readonly logger = new Logger(TokenService.name);
    private readonly SV_ADMIN_URL = "www.sv288.com";

    constructor(
        private readonly httpService: HttpService, 
        private readonly hashService: HashService,
        private readonly settingsService: SettingsService,
        private readonly telegramService: TelegramService,
        private readonly proxyService: ProxyService,
        ) {}

    async getInitialTokens(proxy: string): Promise<{ JSESSIONID: string; SESSION_KEY: string } | null> {
        const url = 'https://'+this.SV_ADMIN_URL+'/';
        const proxyAgent = new HttpsProxyAgent("http://"+proxy);

        const headers = {
            accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
            'accept-language': 'en-US,en;q=0.9,vi;q=0.8',
            'cache-control': 'max-age=0',
            'sec-ch-ua': '"Not A Brand";v="8", "Chromium";v="120", "Google Chrome";v="120"',
            'sec-ch-ua-mobile': '?0',
            'sec-ch-ua-platform': '"Windows"',
            'sec-fetch-dest': 'document',
            'sec-fetch-mode': 'navigate',
            'sec-fetch-site': 'none',
            'sec-fetch-user': '?1',
            'upgrade-insecure-requests': '1',
            'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
        };

        try {
            const response = await firstValueFrom(
                this.httpService.post(url, {}, { headers, httpsAgent: proxyAgent })
            );

            const cookies = response.headers['set-cookie'] || [];
            let jsessionId: string | null = null;

            for (const cookie of cookies) {
                if (cookie.includes('JSESSIONID')) {
                    const match = cookie.match(/JSESSIONID=([^;]+)/);
                    if (match) {
                        jsessionId = match[1];
                        break;
                    }
                }
            }

            // Parse the body for the session key
            const domParser = new xmldom.DOMParser();
            const document = domParser.parseFromString(response.data, 'text/html');
            const sessionKeyElement = document.getElementById('sessionKey');
            const sessionKeyValue = sessionKeyElement ? sessionKeyElement.getAttribute('value') : null;

            if (jsessionId && sessionKeyValue) {
                return {
                    JSESSIONID: jsessionId,
                    SESSION_KEY: sessionKeyValue,
                };
            }

            return null;
        } catch (error) {
            console.error('Error fetching initial tokens:', error);
            this.telegramService.sendMessage(ChatId.GROUP_LOGGING, "Error fetching initial tokens: "+error);
            return null;
        }
    }

    async getValidatedToken(username: string, password: string, proxy: string): Promise<TokenResObj> {
        const tokens = await this.getInitialTokens(proxy);
        if (!tokens) {
          this.logger.error('Error fetching initial tokens.');
          return { status: false, token: null };
        }

        const proxyAgent = new HttpsProxyAgent("http://"+proxy);

        
        const hashedPassword = this.hashService.hashPassword(password, tokens.SESSION_KEY);
        if (!hashedPassword) {
          this.logger.error('Error hashing the password.');
          return { status: false, token: null };
        }
    
        const url = 'https://'+this.SV_ADMIN_URL+'/auth/agent/login';
        const headers = {
          'accept': 'application/json, text/javascript, */*; q=0.01',
          'accept-language': 'en-US,en;q=0.9,vi;q=0.8',
          'content-type': 'application/x-www-form-urlencoded; charset=UTF-8',
          'cookie': `JSESSIONID=${tokens.JSESSIONID};`,
          'origin': 'https://'+this.SV_ADMIN_URL,
          'referer': 'https://'+this.SV_ADMIN_URL+'/',
          'sec-ch-ua': '"Google Chrome";v="119", "Chromium";v="119", "Not?A_Brand";v="24"',
          'sec-ch-ua-mobile': '?0',
          'sec-ch-ua-platform': '"Windows"',
          'sec-fetch-dest': 'empty',
          'sec-fetch-mode': 'cors',
          'sec-fetch-site': 'same-origin',
          'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36',
          'x-requested-with': 'XMLHttpRequest',
        };
    
        const data = qs.stringify({
          userID: username,
          password: hashedPassword,
          timeZone: '-240',
          rememberMe: 'false',
          deviceId: '599e247dfef4d2f57c191ddd02a9b6d5',
        });
    
        try {
          const response = await firstValueFrom(
            this.httpService.post(url, data, { headers, validateStatus: () => true, httpsAgent: proxyAgent }),
          );
    
          const responseData = response.data;
          if (responseData && responseData.status === '200') {
            this.logger.verbose(responseData);
            return { status: true, token: tokens.JSESSIONID };
          } else {
            this.logger.error('Incorrect response from authentication service.');
            return { status: false, token: null };
          }
        } catch (error) {
          this.logger.error(`CURL error: ${error.message}`);
          this.telegramService.sendMessage(ChatId.GROUP_LOGGING, "Error fetching validated tokens: "+error.message);
          return { status: false, token: null };
        }
    }

    async getActiveToken() : Promise<string | null>{
        const active_token = await this.settingsService.findOne("active_token");
        if(!active_token){
            const activeUserName = await this.settingsService.findOne("active_user_name");
            const activeUserPassword = await this.settingsService.findOne("active_user_password");
            if(activeUserName && activeUserPassword){
                const proxy = await this.proxyService.getProxy();
                if(!proxy){
                  this.logger.error('No proxy found');
                  return "PROXY_NOT_FOUND";
                }
                const resData: TokenResObj = await this.getValidatedToken(activeUserName.value, activeUserPassword.value, proxy);
                this.settingsService.upsert("active_token", resData.token)
                return resData.token;
            }
        }
        return active_token.value;
    }

    @Cron(CronExpression.EVERY_2_HOURS, {name: "RefreshAdminToken"})
    async refreshToken() {
        const inactiveUserName = await this.settingsService.findOne("inactive_user_name");
        const inactiveUserPassword = await this.settingsService.findOne("inactive_user_password");
        if(inactiveUserName && inactiveUserPassword){ 
          let resData: TokenResObj;
          try{
            const proxy = await this.proxyService.getProxy();
            if(!proxy){
              this.logger.error('No proxy found');
              return {
                status:  "PROXY_NOT_FOUND",
              }
            }
            resData = await this.getValidatedToken(inactiveUserName.value, inactiveUserPassword.value, proxy);
          }catch(error){
            this.telegramService.sendMessage(ChatId.GROUP_LOGGING, "Refresh Bot failed with error "+ JSON.stringify(error));
          }
          if(resData && resData.status){
              await this.settingsService.upsert("active_token",resData.token);
              const activeUserName = await this.settingsService.findOne("active_user_name");
              const activeUserPassword = await this.settingsService.findOne("active_user_password");
              if(activeUserName && activeUserPassword){
                  await this.settingsService.upsertBulk([
                      {key: "active_token", value: resData.token},
                      {key: "inactive_user_name", value: activeUserName.value},
                      {key: "inactive_user_password", value: activeUserPassword.value},
                      {key: "active_user_password", value: inactiveUserPassword.value},
                      {key: "active_user_name", value: inactiveUserName.value}
                  ]);
              }
              this.telegramService.sendMessage(ChatId.GROUP_LOGGING, "Refresh Bot: 2 hour referesh success");
          }else{
              this.telegramService.sendMessage(ChatId.GROUP_LOGGING, "Refresh Bot: 2 hour referesh failed: "+ JSON.stringify(resData) );
          }
        }else {
            this.telegramService.sendMessage(ChatId.GROUP_LOGGING, "Refresh Bot: 2 hour referesh failed: Missing user cred");
        }
    }


    async testTelegram(): Promise<void>{
       this.telegramService.sendMessage(ChatId.GROUP_MAIN_CPC, "Hello, Lets test Logging Bot")
    }

}

