import { connect } from "mongoose";
import EmployeesList from "../../Models/Employees/Employee";
import { EmployeeRoles } from "../../Models/Employees/Employees.Interface";
import AppointmentList from "../../Models/Appointments/Appointments";
import { DashboardFinancialsResult, DashboardSummaryResult, GetDashboardFinancialsParams, IncomeDataPoint } from "./Dashboard.Interface";
import dayjs from "dayjs";
import { AppointmentEventByClient } from "../../Models/Appointments/Appointments.Interface";
import { validatePermissionsByCompany, validateSessionUser } from "../../helpers/check";
import CashFlowList from "../../Models/CashFlow/CashFlow";
import CompaniesList from "../../Models/Companies/Companies";
import PlanSubscriptionsList from "../../Models/PlanSubscriptions/PlanSubscriptons";
import MetricsList from "../../Models/Metrics/Metrics";

export class DashboardService {
    public async getDashboardSummary(sessionUser: string): Promise<DashboardSummaryResult> {
        await connect(`${process.env.DATABASE_CONNECTION}`);

        await validateSessionUser({ sessionUser });

        // 1. Encontrar todas las relaciones de empleado para este usuario
        const employeeRecords = await EmployeesList.employees.find({ userId: sessionUser });

        let finalRole: "ADMIN" | "EMPLOYEE" | "NONE" = "NONE";
        let allPersonalAgenda: AppointmentEventByClient[] = [];

        for (const emp of employeeRecords) {
            const isAdmin = emp.roles?.includes(EmployeeRoles.ADMIN) || false;
            if (isAdmin && finalRole !== "ADMIN") {
                finalRole = "ADMIN";
            } else if (!isAdmin && finalRole === "NONE") {
                finalRole = "EMPLOYEE";
            }

            const todayStart = dayjs().startOf("day").toDate();
            const todayEnd = dayjs().endOf("day").toDate();

            if (isAdmin) {
                // If ADMIN, fetch all appointments for this company for today
                const apps = await AppointmentList.Appointments.AppointmentList.find({
                    companyId: emp.companyId,
                    start: { $gte: todayStart, $lte: todayEnd }
                });
                for (const a of apps) {
                    try {
                        const evt = await AppointmentList.getAppointmentEvent({
                            appointmentId: String(a._id),
                            sessionUser: sessionUser
                        });
                        allPersonalAgenda.push(evt);
                    } catch (e) {
                        console.error(e);
                    }
                }
            } else {
                // If EMPLOYEE, fetch only their appointments for today
                const apps = await AppointmentList.Appointments.AppointmentList.find({
                    companyId: emp.companyId,
                    employeeId: String(emp.id),
                    start: { $gte: todayStart, $lte: todayEnd }
                });
                for (const a of apps) {
                    try {
                        const evt = await AppointmentList.getAppointmentEvent({
                            appointmentId: String(a._id),
                            sessionUser: sessionUser
                        });
                        allPersonalAgenda.push(evt);
                    } catch (e) {
                        console.error(e);
                    }
                }
            }
        }

        // Sort agenda by start time
        allPersonalAgenda.sort((a, b) => a.startTime - b.startTime);

        // Note: globalMetrics is handled by the frontend useSessionStore
        // we just return the role and the personalAgenda
        return {
            role: finalRole,
            personalAgenda: allPersonalAgenda
        };
    }

    public async getDashboardFinancials(params: GetDashboardFinancialsParams): Promise<DashboardFinancialsResult> {
        await connect(`${process.env.DATABASE_CONNECTION}`);

        await validateSessionUser({ sessionUser: params.sessionUser });
        await validatePermissionsByCompany({ companyId: params.companyId, sessionUser: params.sessionUser });

        const now = dayjs();
        let startDate: Date;
        let prevStartDate: Date;
        let endDate = now.toDate();
        let prevEndDate: Date;

        if (params.timeframe === "week") {
            startDate = now.subtract(7, "day").startOf("day").toDate();
            prevStartDate = now.subtract(14, "day").startOf("day").toDate();
            prevEndDate = now.subtract(7, "day").endOf("day").toDate();
        } else if (params.timeframe === "month") {
            startDate = now.subtract(1, "month").startOf("day").toDate();
            prevStartDate = now.subtract(2, "month").startOf("day").toDate();
            prevEndDate = now.subtract(1, "month").endOf("day").toDate();
        } else { // year
            startDate = now.subtract(1, "year").startOf("month").toDate();
            prevStartDate = now.subtract(2, "year").startOf("month").toDate();
            prevEndDate = now.subtract(1, "year").endOf("month").toDate();
        }

        // Fetch current period cashflow
        const currentMatch: any = {
            companyId: params.companyId,
            type: "deposit",
            createdAt: { $gte: startDate, $lte: endDate }
        };
        if (params.employeeId) currentMatch.employeeId = params.employeeId;
        
        const currentMovements = await CashFlowList.movements.movementList.find(currentMatch);
        
        // Fetch previous period cashflow
        const prevMatch: any = {
            companyId: params.companyId,
            type: "deposit",
            createdAt: { $gte: prevStartDate, $lte: prevEndDate }
        };
        if (params.employeeId) prevMatch.employeeId = params.employeeId;
        
        const prevMovements = await CashFlowList.movements.movementList.find(prevMatch);

        const currentPeriodTotal = currentMovements.reduce((sum, m) => sum + m.amount, 0);
        const previousPeriodTotal = prevMovements.reduce((sum, m) => sum + m.amount, 0);

        // Group data points
        const dataPointsMap = new Map<string, number>();
        
        currentMovements.forEach(m => {
            let key = "";
            if (params.timeframe === "year") {
                key = dayjs(m.createdAt).format("YYYY-MM");
            } else {
                key = dayjs(m.createdAt).format("YYYY-MM-DD");
            }
            dataPointsMap.set(key, (dataPointsMap.get(key) || 0) + m.amount);
        });

        // Fill empty dates for the chart
        const currentPeriodData: IncomeDataPoint[] = [];
        if (params.timeframe === "year") {
            for (let i = 0; i < 12; i++) {
                const k = dayjs(startDate).add(i, "month").format("YYYY-MM");
                currentPeriodData.push({ date: k, amount: dataPointsMap.get(k) || 0 });
            }
        } else {
            const days = params.timeframe === "week" ? 7 : 30; // Approx
            for (let i = 0; i <= days; i++) {
                const k = dayjs(startDate).add(i, "day").format("YYYY-MM-DD");
                if (dayjs(k).isAfter(now)) break;
                currentPeriodData.push({ date: k, amount: dataPointsMap.get(k) || 0 });
            }
        }

        // Calculate unpaid today
        const todayStart = now.startOf("day").toDate();
        const todayEnd = now.endOf("day").toDate();
        
        const unpaidMatch: any = {
            companyId: params.companyId,
            payment: false,
            start: { $gte: todayStart, $lte: todayEnd }
        };
        if (params.employeeId) unpaidMatch.employeeId = params.employeeId;

        const unpaidApps = await AppointmentList.Appointments.AppointmentList.find(unpaidMatch);
        const unpaidToday = unpaidApps.reduce((sum, a) => sum + (a.price || 0), 0);

        return {
            currentPeriodTotal,
            previousPeriodTotal,
            currentPeriodData,
            unpaidToday
        };
    }

    public async getCompanyMetrics(params: { sessionUser: string; companyId: string }): Promise<any> {
        await connect(`${process.env.DATABASE_CONNECTION}`);

        await validateSessionUser({ sessionUser: params.sessionUser });
        await validatePermissionsByCompany({ companyId: params.companyId, sessionUser: params.sessionUser });

        const company = await CompaniesList.companies.findOne({ _id: params.companyId });
        if (!company) {
            throw new Error("No se encontro la compañia.");
        }

        const ownerId = company.ownerId;
        const isOwner = String(ownerId) === String(params.sessionUser);

        // Fetch metrics for owner
        const metrics = await MetricsList.getMetrics(String(ownerId));
        
        // Fetch subscription for owner
        const subscription = await PlanSubscriptionsList.getSubscriptionByUser({ sessionUser: String(ownerId) });

        return {
            metrics,
            subscription,
            isOwner
        };
    }
}
