import { parentPort } from 'node:worker_threads'
import { PeerInfo, Question, QuestionDb } from '../../types/basicTypes'
import { files, paths, readAndValidateFile } from '../../utils/files'
import utils from '../../utils/utils'
import { RecievedData } from '../../utils/actions'
import { removeCacheFromQuestion } from '../../utils/qdbUtils'
import { QuestionAddResponse } from '../../modules/api/submodules/qminingapi'
import logger from '../../utils/logger'
import {
    loginToPeer,
    peerToString,
    updatePeersFile,
} from '../../utils/p2putils'
import { post } from '../../utils/networkUtils'

const login = async (peer: PeerInfo): Promise<string> => {
    const loginResult = await loginToPeer(peer)
    if (typeof loginResult === 'string') {
        return loginResult
    } else {
        return null
    }
}

export type QuestionsToPeersTaskObject = {
    type: 'sendQuestionsToPeers'
    data: {
        newQuestions: Question[]
        location: string
        subj: string
    }
}

export const handleQuestionsToPeers = async (
    _qdbs: QuestionDb[],
    msg: QuestionsToPeersTaskObject,
    workerIndex: number
): Promise<void> => {
    const { newQuestions, location, subj } = msg.data

    const domain = utils.ReadFile(paths.domainFile).trim()
    const peers = readAndValidateFile<PeerInfo[]>(files.peersFile)

    if (!peers || peers.length === 0 || newQuestions.length === 0) {
        parentPort.postMessage({
            msg: `From thread #${workerIndex}: sendQuestionsToPeers done`,
            workerIndex: workerIndex,
        })
        return
    }

    const dataToSend: RecievedData = {
        fromPeer: true,
        subj: subj,
        location: location,
        id: domain, // client ID
        version: 'P2P',
        quiz: newQuestions.map((question) => {
            return removeCacheFromQuestion({
                ...question,
                data: {
                    ...question.data,
                    source: domain,
                },
            })
        }),
    }

    const results: {
        errors: PeerInfo[]
        hasNew: PeerInfo[]
        sent: PeerInfo[]
        loginErrors: PeerInfo[]
    } = {
        errors: [],
        hasNew: [],
        sent: [],
        loginErrors: [],
    }

    const postData = (peer: PeerInfo, sessionCookie: string) => {
        return post<QuestionAddResponse>({
            hostname: peer.host,
            port: peer.port,
            http: peer.http,
            path: '/api/isAdding',
            bodyObject: dataToSend,
            cookie: `sessionID=${sessionCookie}`,
        })
    }

    for (const peer of peers) {
        let sessionCookie = peer.sessionCookie

        if (!sessionCookie) {
            sessionCookie = await login(peer)
            if (!sessionCookie) {
                results.loginErrors.push(peer)
                continue
            }
            updatePeersFile(peers, { ...peer, sessionCookie: sessionCookie })
        }

        let res = await postData(peer, sessionCookie)

        if (res.data?.result === 'nouser' && sessionCookie) {
            sessionCookie = await login(peer)
            if (!sessionCookie) {
                results.loginErrors.push(peer)
                continue
            }
            updatePeersFile(peers, { ...peer, sessionCookie: sessionCookie })
            res = await postData(peer, sessionCookie)
        }

        if (res.error || !res.data?.success) {
            results.errors.push(peer)
            console.error('AAAAAAAAAAAAAAAAAAAAAAA')
            console.error(res.error || JSON.stringify(res.data))
        } else if (res.data?.totalNewQuestions > 0) {
            results.hasNew.push(peer)
        } else {
            results.sent.push(peer)
        }
    }

    const logMsg: string[] = []
    const addToLogMsg = (
        peerResult: PeerInfo[],
        prefix: string,
        color: string
    ) => {
        if (peerResult.length > 0) {
            logMsg.push(
                `${logger.C(color)}${prefix}:${logger.C()} ` +
                    peerResult.map((x) => peerToString(x)).join(', ')
            )
        }
    }
    addToLogMsg(results.loginErrors, 'Login error', 'red')
    addToLogMsg(results.errors, 'Error', 'red')
    addToLogMsg(results.hasNew, 'Had new questions', 'blue')
    addToLogMsg(results.sent, 'Sent', 'green')

    logger.Log(
        `\t${logger.C(
            'green'
        )}Sent new questions to peers${logger.C()}; ${logMsg.join(', ')}`
    )

    parentPort.postMessage({
        msg: `From thread #${workerIndex}: sendQuestionsToPeers done`,
        workerIndex: workerIndex,
    })
}
