import { useState } from 'react';
import { csrfToken } from '@/lib/csrf';
import {
    client as generateClientAdvice,
    mine as generateMyAdvice,
} from '@/routes/growth-score/advice';
import type { AdvisorAdvice, SavedAdvisorAdvice } from '@/types/growth-score';

export type GrowthExternalCompany = {
    id: string;
    accountType: string;
    name: string;
};

export type GrowthAdviceState = {
    advice: AdvisorAdvice | null;
    generatedAt: string | null;
    isStale: boolean;
    loading: boolean;
    error: string | null;
    generateAdvice: () => Promise<void>;
};

function isAdvisorAdvice(value: unknown): value is AdvisorAdvice {
    if (typeof value !== 'object' || value === null) {
        return false;
    }

    const advice = value as Record<string, unknown>;
    const plan = advice.three_month_plan;
    const projection = advice.next_grade_projection;
    const upskill = advice.upskill_recommendation;

    return (
        Array.isArray(advice.critical_must_do) &&
        Array.isArray(advice.areas_needing_focus) &&
        Array.isArray(advice.good_to_have) &&
        typeof plan === 'object' &&
        plan !== null &&
        typeof projection === 'object' &&
        projection !== null &&
        typeof upskill === 'object' &&
        upskill !== null
    );
}

export function extractAdvice(payload: unknown): AdvisorAdvice | null {
    if (!isAdvisorAdvice(payload)) {
        return null;
    }

    const legacyPlan = payload.three_month_plan;
    const legacyProjection = payload.next_grade_projection;

    return {
        critical_must_do: payload.critical_must_do,
        areas_needing_focus: payload.areas_needing_focus,
        good_to_have: payload.good_to_have,
        three_month_plan: payload.three_month_plan,
        ninety_day_plan: payload.ninety_day_plan ?? {
            days_0_30: legacyPlan.month_1,
            days_31_60: legacyPlan.month_2,
            days_61_90: legacyPlan.month_3,
        },
        next_grade_projection: {
            ...legacyProjection,
            months_12: legacyProjection.months_12 ?? legacyProjection.months_6,
        },
        upskill_recommendation: payload.upskill_recommendation,
    };
}

export function useGrowthAdvice({
    savedAdvice,
    externalCompany,
}: {
    savedAdvice?: SavedAdvisorAdvice | null;
    externalCompany?: GrowthExternalCompany;
}): GrowthAdviceState {
    const [advice, setAdvice] = useState<AdvisorAdvice | null>(
        savedAdvice ? extractAdvice(savedAdvice) : null,
    );
    const [generatedAt, setGeneratedAt] = useState<string | null>(
        savedAdvice?.generated_at ?? null,
    );
    const [isStale, setIsStale] = useState(savedAdvice?.is_stale ?? false);
    const [loading, setLoading] = useState(false);
    const [error, setError] = useState<string | null>(null);

    async function generateAdvice(): Promise<void> {
        setLoading(true);
        setError(null);

        try {
            const url = externalCompany
                ? generateClientAdvice.url(externalCompany.id)
                : generateMyAdvice.url();

            const response = await fetch(url, {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                    Accept: 'application/json',
                    'X-XSRF-TOKEN': csrfToken(),
                    'X-Requested-With': 'XMLHttpRequest',
                },
                credentials: 'same-origin',
                body: JSON.stringify(
                    externalCompany
                        ? { account_type: externalCompany.accountType }
                        : {},
                ),
            });

            const payload = (await response.json()) as Record<
                string,
                unknown
            > & {
                message?: string;
                errors?: Record<string, string[]>;
                generated_at?: string;
                is_stale?: boolean;
            };

            if (!response.ok) {
                throw new Error(
                    payload.message ??
                        Object.values(payload.errors ?? {})[0]?.[0] ??
                        'Advice could not be generated. Please try again.',
                );
            }

            const nextAdvice = extractAdvice(payload);

            if (nextAdvice === null) {
                throw new Error(
                    'The advisor returned an empty response. Please try again.',
                );
            }

            setAdvice(nextAdvice);
            setGeneratedAt(payload.generated_at ?? null);
            setIsStale(payload.is_stale ?? false);
        } catch (caught) {
            setError(
                caught instanceof Error
                    ? caught.message
                    : 'Advice could not be generated. Please try again.',
            );
        } finally {
            setLoading(false);
        }
    }

    return {
        advice,
        generatedAt,
        isStale,
        loading,
        error,
        generateAdvice,
    };
}
