import { clsx, type ClassValue } from 'clsx';
import { toast } from 'sonner';
import { twMerge } from 'tailwind-merge';
// @ts-ignore: no type declarations available for tz-lookup
import tzLookup from 'tz-lookup';
import momentTz from 'moment-timezone';

export function cn(...inputs: ClassValue[]) {
    return twMerge(clsx(inputs));
}

export const isEmpty = (data: any): boolean => {
    if (data == null) {
        return true;
    }

    if (typeof data === 'string') {
        return ['', null, 'null', undefined].includes(data);
    }

    if (Array.isArray(data)) {
        return data.length === 0;
    }

    if (typeof data === 'object') {
        return Object.keys(data).length === 0;
    }

    // if (typeof data === 'number') {
    //     return data === 0
    // }
    return data.length === 0;
};

export const getToken = () => {
    if (typeof window !== 'undefined') {
        return localStorage.getItem('access_token');
    }
    return null;
};

export const getFCMToken = () => {
    if (typeof window !== 'undefined') {
        return localStorage.getItem('fcm_token');
    }
    return null;
};

export const setFCMToken = (token: string) => {
    if (typeof window !== 'undefined') {
        localStorage.setItem('fcm_token', token);
    }
};

export const removeFCMToken = () => {
    if (typeof window !== 'undefined') {
        localStorage.removeItem('fcm_token');
    }
};

export const handleAuthentication = () => {
    const token = getToken();
    if (!token) {
        // Redirect to login page if token is not found
        if (typeof window !== 'undefined') {
            window.location.href = '/login';
        }
        throw new Error('No authentication token found');
    }
    return token;
};

export const handleToastError = (response: any) => {
    if (!response?.data?.success) {
        const status = response?.data?.status ?? 0;

        const code = response?.data?.code ?? null;

        if (code && [1001, 1002, 1003, 422, 500, 400].includes(code)) {
            !isEmpty(response?.data?.message) && toast.error(response?.data?.message);
        }

        if (status === 400) {
            toast.error('Bad Request');
        }

        if (status === 422) {
            const error = response?.data?.errors ?? [];
            const message = error.length > 0 ? error[0]?.message : '';

            !isEmpty(message) && toast.error(message);
        }
    }

    if (response?.data?.statusCode === 500) {
        toast.error('Internal server error');
    }
};

const isNumString = (str: string): boolean => !isNaN(Number(str));

type JsonObject = { [key: string]: unknown };
type JsonArray = Array<unknown>;
type Json = JsonObject | JsonArray | string | number | boolean | null;

function deepParseJson(jsonString: Json): Json {
    if (typeof jsonString === 'string') {
        if (isNumString(jsonString)) {
            return jsonString;
        }
        try {
            return deepParseJson(JSON.parse(jsonString));
        } catch (err) {
            return jsonString;
        }
    } else if (Array.isArray(jsonString)) {
        return jsonString.map((val) => deepParseJson(val as JsonArray));
    } else if (typeof jsonString === 'object' && jsonString !== null) {
        return Object.keys(jsonString).reduce<JsonObject>((obj, key) => {
            const val = jsonString[key];
            obj[key] = isNumString(val as string) ? val : deepParseJson(val as number);
            return obj;
        }, {});
    } else {
        return jsonString;
    }
}

export function calculateAge(dob: string): number {
    if (!dob) return 0;

    const birthDate = new Date(dob);
    const today = new Date();

    let age = today.getFullYear() - birthDate.getFullYear();
    const monthDiff = today.getMonth() - birthDate.getMonth();
    const dayDiff = today.getDate() - birthDate.getDate();

    // Adjust age if the birthday hasn't occurred yet this year
    if (monthDiff < 0 || (monthDiff === 0 && dayDiff < 0)) {
        age--;
    }

    return age < 0 ? 0 : age; // In case DOB is in future
}

export default deepParseJson;

export const safeText = (text: any) => (isEmpty(text) ? 'N/A' : text);

export const parseDurationString = (durationStr: string) => {
    let hours = 0;
    let minutes = 0;

    const hourMatch = durationStr.match(/(\d+)\s*hour/);
    const minMatch = durationStr.match(/(\d+)\s*min/);

    if (hourMatch) hours = parseInt(hourMatch[1], 10);
    if (minMatch) minutes = parseInt(minMatch[1], 10);

    return hours * 60 + minutes; // return total minutes
};

export const STATUS = {
    REQUESTED: 'requested',
    SCHEDULED: 'scheduled',
    ONGOING: 'ongoing',
    COMPLETED: 'completed',
    CANCELLED: 'cancelled',
    PICKUP: 'pickup',
    DRAFT: 'draft',
    REQUESTED_BY_CUSTOMER: 'req. by customer',
    INVOICED: 'invoiced',
};

export const getTimezone = (lat: any, lng: any) => {
    try {
        const timezone = tzLookup(lat, lng);
        return timezone;
    } catch (err) {
        console.error('Error finding timezone:', err);
        return 'America/New_York'; // Default fallback
    }
};

export const getGMTOffset = (tz?: string) => {
    if (!tz) return '';
    const offset = momentTz.tz(tz).format('Z');
    return `GMT${offset}`;
};

export const formatDateTime = (date: any, timezone?: string) => {
    if (!date) return '';

    if (timezone) {
        return momentTz.tz(date, timezone).format('MM-DD-YYYY hh:mm A');
    }

    return momentTz(date).format('MM-DD-YYYY hh:mm A');
};

/**
 * Converts a date (string, timestamp, or Date object) to "dd/MM/yyyy" format.
 * Use for date-only display where timezone is not considered.
 * @param date - Date string, timestamp (ms), or Date object
 * @returns Formatted date string or empty string if invalid
 */
export const formatDate = (date: string | number | Date | null | undefined): string => {
    const m = momentTz(date);
    return m.isValid() ? m.format('MM/DD/YYYY') : '';
};
