"use server";

import {createAdminClient} from "@/lib/backend/server-appwrite";
import {Session, SessionMessage, UserProfile} from "@/lib/backend/appwrite_types";
import {ID, Query} from "node-appwrite";
import {sortByCreatedAtDesc} from "@/lib/backend/utils";

export type SessionMessageDto = {
    senderId: string;
    recipientId: string;
    message: string;
    read: boolean;
}


export async function sendSessionMessage(
    session: string,
    sessionMessageToBeSent: SessionMessageDto
) {
    const {databases} = await createAdminClient();

    let message = {
        session: session,
        senderId: sessionMessageToBeSent.senderId,
        recipientId: sessionMessageToBeSent.recipientId,
        message: sessionMessageToBeSent.message,
        read: sessionMessageToBeSent.read
    }

    try {
        let sessionMessage = await databases.createDocument(
            process.env.NEXT_APPWRITE_DATABASE_ID!,
            process.env.NEXT_APPWRITE_SESSION_MESSAGES_COLLECTION_ID!,
            {...message}
        );

        // update the last message id in chats db
        await databases.updateDocument(
            process.env.NEXT_APPWRITE_DATABASE_ID!,
            process.env.NEXT_APPWRITE_SESSIONS_COLLECTION_ID!,
            session,
            {
                changeLog: `message/create/${sessionMessage.$id}`,
                changerId: sessionMessageToBeSent.senderId
            }
        )

        return sessionMessage as SessionMessage;

    } catch (error: any) {
        throw error;
    }

}


export async function getUserSessions(userId: string) {
    const {databases} = await createAdminClient();

    // get the user profile, has a relationship to the user chat sessions
    const userProfile = await databases.getDocument(
        process.env.NEXT_APPWRITE_DATABASE_ID!,
        process.env.NEXT_APPWRITE_USERS_COLLECTION_ID!,
        userId
    ) as UserProfile

    // get the ids of the user sessions
    const sessionIds = userProfile.sessions?.map((session) => session.$id);

    let sessions: Session[] = [];
    if (sessionIds?.length > 0) {
        let {documents} = await databases.listDocuments(
            process.env.NEXT_APPWRITE_DATABASE_ID!,
            process.env.NEXT_APPWRITE_SESSIONS_COLLECTION_ID!,
            [
                Query.equal("$id", [...sessionIds]),
                Query.orderDesc("$updatedAt"),
                Query.limit(10),
            ]
        )

        sessions = documents as Session[]
    }

    return sessions as Session[]

}

export async function getSessionById(sessionId: string) {
    const {databases} = await createAdminClient();

    try {
        let session = await databases.getDocument(
            process.env.NEXT_APPWRITE_DATABASE_ID!,
            process.env.NEXT_APPWRITE_SESSIONS_COLLECTION_ID!,
            sessionId
        )
        return session as Session
    } catch (e) {
        throw new Error("Error getting session by id");
    }
}


export async function getSessionMessages(sessionId: string) {
    const {databases} = await createAdminClient();
    const session = await databases.getDocument(
        process.env.NEXT_APPWRITE_DATABASE_ID!,
        process.env.NEXT_APPWRITE_SESSIONS_COLLECTION_ID!,
        sessionId
    ) as Session;

    return session.sessionMessages.toSorted(
        sortByCreatedAtDesc
    ) as SessionMessage[];
}


export async function createNewSession(senderId: string, receiverId: string) {
    const {databases} = await createAdminClient();

    // get all user sessions
    let sessions = await getUserSessions(senderId);
    let result: Session = sessions.find(
        (session) =>
            session.participants.length === 2 &&
            session.participants.some(
                (participant) => participant.$id === receiverId,
            ),
    );

    if (result) return {...result, existed: true};
    let sessionDoc = await databases.createDocument(
        process.env.NEXT_APPWRITE_DATABASE_ID!,
        process.env.NEXT_APPWRITE_SESSIONS_COLLECTION_ID!,
        ID.unique(),
        {
            // twoWay manyToMany
            participants: [senderId, receiverId],
        },
    ) as Session;

    let user = sessionDoc.participants.find(
        (participant: any) => participant.$id === senderId,
    );

    await sendSystemMessage(
        process.env.NEXT_APPWRITE_DATABASE_ID!,
        process.env.NEXT_APPWRITE_SESSION_MESSAGES_COLLECTION_ID!,
        {
            session: sessionDoc.$id,
            senderId: "system",
            recipientId: "system",
            message: `${user?.fullName} created this chat. You can now start chatting`,
            read: true
        }
    )

    await databases.updateDocument(
        process.env.NEXT_APPWRITE_DATABASE_ID!,
        process.env.NEXT_APPWRITE_USERS_COLLECTION_ID!,
        receiverId,
        {changeLog: `conversations/create/${sessionDoc.$id}`, changerId: senderId}
    )

    return {
        ...sessionDoc as Session,
        existed: false
    };

}



export async function deleteSession(sessionId: string, receiverId: string) {
    const {databases} = await createAdminClient();

    await clearSessionMessages(sessionId);
    await databases.deleteDocument(
        process.env.NEXT_APPWRITE_DATABASE_ID!,
        process.env.NEXT_APPWRITE_SESSIONS_COLLECTION_ID!,
        sessionId,
    );

    await databases.updateDocument(
        process.env.NEXT_APPWRITE_DATABASE_ID!,
        process.env.NEXT_APPWRITE_USERS_COLLECTION_ID!,
        receiverId,
        { changeLog: `conversations/delete/${sessionId}` },
    );
}



export async function clearSessionMessages(sessionId: string, clearer?: UserProfile) {
    const {databases} = await createAdminClient();

    // get all messages for this session
    const messages = await getSessionMessages(sessionId);
    if (messages.length === 0) return;

    // delete all messages
    messages.forEach((message) => {
        databases.deleteDocument(
            process.env.NEXT_APPWRITE_DATABASE_ID!,
            process.env.NEXT_APPWRITE_SESSION_MESSAGES_COLLECTION_ID!,
            message.$id
        ).catch((e) => {
        })
    })

    // send broadcast to channel subscription
    await databases.updateDocument(
        process.env.NEXT_APPWRITE_DATABASE_ID!,
        process.env.NEXT_APPWRITE_SESSIONS_COLLECTION_ID!,
        sessionId,
        {
            changeLog: `message/clearAll`,
            changerId: clearer?.$id
        }
    )
}


export async function sendSystemMessage(
    databaseId: string,
    collectionId: string,
    message: any,
) {
    const {databases} = await createAdminClient();

    await databases.createDocument(databaseId, collectionId, ID.unique(), {
        ...message,
        senderId: "system",
        read: true,
    });
}


