import { UserDAO } from '../../../../users/infrastructure/dataAccessObjects/userDAO';
import {
    HourCategory,
    HourCategoryForCreation,
    HourCategoryForUpdate,
    HourCategorizationRule,
    HourCategorizationRuleForCreation,
    RuleCondition,
} from '../../../business/models/hourCategorization';
import {
    TimeTrackingCategorizedHourWithCategoryData,
    TimeTrackingCategorizedHourWithUserData,
} from '../../../business/models/timeTracking/timeTrackingCategorizedHour';
import { TimeTrackingPolicyHourCategory } from '../../../business/models/TimeTrackingPolicyHourCategory';
import { CreateHourCategoryVC } from '../../../presentation/validationClasses/hourCategorization/createHourCategoryVC';
import { RuleLogicVC, RuleConditionVC } from '../../../presentation/validationClasses/hourCategorization/rulesVC';
import { UpdateHourCategoryVC } from '../../../presentation/validationClasses/hourCategorization/updateHourCategoryVC';
import { TimeTrackingCategorizedHourDAO } from '../../dataAccessObjects/hourCategory/categorizedHour';
import { TimeTrackingHourCategorizationRuleDAO } from '../../dataAccessObjects/hourCategory/hourCategorizationRule';
import { TimeTrackingHourCategoryDAO } from '../../dataAccessObjects/hourCategory/hourCategory';

export const mapHourCategoryDAOToModel = (dao: TimeTrackingHourCategoryDAO): HourCategory => {
    const rules = (dao.rules ?? []).map(mapHourCategorizationRuleDAOToModel);
    return new HourCategory({
        description: dao.description ?? undefined,
        effectiveFrom: dao.effectiveFrom,
        effectiveTo: dao.effectiveTo ?? undefined,
        id: dao.id,
        instanceId: dao.instanceId,
        name: dao.name,
        rules,
    });
};

export const mapHourCategorizationRuleDAOToModel = (dao: TimeTrackingHourCategorizationRuleDAO): HourCategorizationRule => {
    const conditions = (dao.logic?.conditions ?? []).map((c) => ({
        field: c.field,
        operator: c.operator,
        value: c.value,
        valueType: c.valueType,
    })) as RuleCondition[];

    return new HourCategorizationRule({
        conditions,
        hourCategoryId: dao.hourCategoryId,
        id: dao.id,
        operator: dao.logic.operator,
    });
};

export const mapHourCategorizationRuleModelToDAO = (
    rule: HourCategorizationRuleForCreation,
    hourCategoryId: number,
): Partial<TimeTrackingHourCategorizationRuleDAO> => ({
    hourCategoryId,
    logic: {
        conditions: rule.conditions.map((c: RuleCondition) => ({
            field: c.field,
            operator: c.operator,
            value: c.value,
            valueType: c.valueType,
        })),
        operator: rule.operator,
    },
});

export const mapCreateHourCategoryVCToDTO = (vc: CreateHourCategoryVC, instanceId: number): HourCategoryForCreation => {
    return {
        description: vc.description,
        effectiveFrom: vc.effectiveFrom,
        instanceId,
        name: vc.name,
        rules: vc.rules.map(mapRuleLogicVCToRuleForCreation),
    };
};

export const mapUpdateHourCategoryVCToDTO = (vc: UpdateHourCategoryVC, instanceId: number): HourCategoryForUpdate => {
    return {
        description: vc.description,
        effectiveFrom: vc.effectiveFrom,
        instanceId,
        name: vc.name,
        rules: vc.rules?.map(mapRuleLogicVCToRuleForCreation),
    };
};

export const mapRuleLogicVCToRuleForCreation = (ruleVC: RuleLogicVC): HourCategorizationRuleForCreation => ({
    conditions: ruleVC.conditions.map(mapRuleConditionVCToCondition),
    operator: ruleVC.operator as 'AND' | 'OR',
});

export const mapRuleConditionVCToCondition = (c: RuleConditionVC): RuleCondition => ({
    field: c.field,
    operator: c.operator,
    value: c.value,
    valueType: c.valueType,
});

export const mapHourCategoryForUpdateToDAO = (dto: HourCategoryForUpdate): Partial<TimeTrackingHourCategoryDAO> => ({
    description: dto.description,
    effectiveFrom: dto.effectiveFrom,
    effectiveTo: dto.effectiveTo,
    instanceId: dto.instanceId,
    name: dto.name,
});

export const mapHourCategoryDAOToPolicyHourCategory = (category: TimeTrackingHourCategoryDAO): TimeTrackingPolicyHourCategory => {
    const policyHourCategory = category.policyHourCategories?.[0];
    return {
        assigned: !!policyHourCategory,
        description: category.description ?? undefined,
        id: category.id,
        instanceId: category.instanceId,
        name: category.name,
        requiresApproval: policyHourCategory?.requiresApproval ?? null,
    };
};

/**
 *  Maps DAO to model; includes category name only (no user data).
 * */
export const mapCategorizedHourDAOToModelWithCategoryData = (dao: TimeTrackingCategorizedHourDAO): TimeTrackingCategorizedHourWithCategoryData => {
    return new TimeTrackingCategorizedHourWithCategoryData({
        dateString: dao.dateString,
        entryIds: dao.entryIds ?? null,
        hourCategoryId: dao.hourCategoryId,
        hourCategoryName: dao.hourCategory?.name ?? '',
        hourCategoryDescription: dao.hourCategory?.description ?? null,
        hours: dao.hours,
        id: dao.id,
        instanceId: dao.instanceId,
        note: dao.note ?? null,
        status: dao.status,
        statusOverriddenByAdminId: dao.statusOverriddenByAdminId ?? null,
        statusOverrideComment: dao.statusOverrideComment ?? null,
        userId: dao.userId,
    });
};

/**
 * Enriches categorized hour rows with user data. Missing users get null fields.
 * **/
export const mapCategorizedHoursWithUserEnrichment = (
    rows: TimeTrackingCategorizedHourWithCategoryData[],
    userSources: UserBasicDataDTO[],
    currentStepIdsByHourId: Map<string, string> = new Map(),
    loggedUserApproverHourIds: Set<string> = new Set(),
): TimeTrackingCategorizedHourWithUserData[] => {
    const userDataByUserId = new Map(
        userSources.map((u) => [
            u.id,
            {
                employeeInternalId: u.employeeInternalId,
                fullName: u.fullName,
                profilePicture: u.profilePicture ?? null,
            },
        ]),
    );
    return rows.map((row) => {
        const userData = userDataByUserId.get(row.userId);
        return new TimeTrackingCategorizedHourWithUserData({
            ...row,
            currentStepId: currentStepIdsByHourId.get(row.id) ?? null,
            employeeInternalId: userData?.employeeInternalId ?? null,
            fullName: userData?.fullName ?? null,
            isLoggedUserPotentialApprover: loggedUserApproverHourIds.has(row.id),
            isStatusOverridden: row.statusOverriddenByAdminId != null,
            profilePicture: userData?.profilePicture ?? null,
        });
    });
};
export type UserBasicDataDTO = {
    id: number;
    employeeInternalId: string;
    fullName: string;
    profilePicture: string | null;
};
export const mapUsersToUserBasicDataDTOs = (users: UserDAO[]): UserBasicDataDTO[] => {
    return users.map((user) => ({
        employeeInternalId: user.employeeInternalId,
        fullName: `${user.firstName} ${user.lastName}`.trim(),
        id: user.id,
        profilePicture: user.profilePicture,
    }));
};
