'use client';
import { classifyLogoShape, logoShapeWarning, resolveAdminBrand, resolvePublicBrand, validateAccentColor, } from '@actuate-media/cms-core/appearance';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { useTheme } from '../../components/ThemeProvider.js';
import { cmsApi } from '../../lib/api.js';
export const FIELD_ROLE = {
    publicPrimaryLogoAssetId: 'publicPrimaryLogo',
    publicLogoMarkAssetId: 'publicLogoMark',
    publicDarkLogoAssetId: 'publicDarkLogo',
    adminPrimaryLogoAssetId: 'adminPrimaryLogo',
    adminLogoMarkAssetId: 'adminLogoMark',
    adminDarkLogoAssetId: 'adminDarkLogo',
    faviconSourceAssetId: 'faviconSource',
};
const LOGO_MIME = ['image/svg+xml', 'image/png'];
const FAVICON_MIME = ['image/svg+xml', 'image/png'];
const MAX_BYTES = 1024 * 1024; // 1 MB
const DEFAULT_FORM = {
    colorMode: 'system',
    accent: 'purple',
    density: 'default',
    font: 'DM Sans',
    reduceMotion: false,
};
const DEFAULT_BRAND_FORM = { mode: 'inheritPublicBrand' };
function snapshot(a, b) {
    return JSON.stringify({ a, b });
}
/** Read an uploaded image's shape client-side (raster via Image, SVG via XML). */
async function detectShape(file) {
    if (file.type === 'image/svg+xml') {
        try {
            const text = await file.text();
            const viewBox = text.match(/viewBox\s*=\s*["']\s*[\d.]+\s+[\d.]+\s+([\d.]+)\s+([\d.]+)/i);
            if (viewBox)
                return classifyLogoShape(parseFloat(viewBox[1]), parseFloat(viewBox[2]));
            const w = text.match(/\bwidth\s*=\s*["']([\d.]+)/i);
            const h = text.match(/\bheight\s*=\s*["']([\d.]+)/i);
            if (w && h)
                return classifyLogoShape(parseFloat(w[1]), parseFloat(h[1]));
        }
        catch {
            /* fall through */
        }
        return 'unknown';
    }
    return new Promise((resolve) => {
        const url = URL.createObjectURL(file);
        const img = new Image();
        img.onload = () => {
            URL.revokeObjectURL(url);
            resolve(classifyLogoShape(img.naturalWidth, img.naturalHeight));
        };
        img.onerror = () => {
            URL.revokeObjectURL(url);
            resolve('unknown');
        };
        img.src = url;
    });
}
function assetToCache(brand, assets, siteTitle) {
    const map = new Map(Object.values(assets).map((a) => [
        a.id,
        { id: a.id, publicUrl: a.url, mimeType: a.mimeType, width: a.width, height: a.height },
    ]));
    const resolved = resolveAdminBrand(brand, map, siteTitle);
    return {
        primaryUrl: resolved.primary?.url ?? null,
        markUrl: resolved.mark?.url ?? null,
        darkUrl: resolved.dark?.url ?? null,
        initials: resolved.initials,
    };
}
export function useAppearanceSettings(canEdit, config) {
    const { applyAppearance, setAdminBrand } = useTheme();
    const [loading, setLoading] = useState(true);
    const [loadError, setLoadError] = useState(null);
    const [appearance, setAppearance] = useState(DEFAULT_FORM);
    const [brand, setBrand] = useState(DEFAULT_BRAND_FORM);
    const [assets, setAssets] = useState({});
    const [saveState, setSaveState] = useState('idle');
    const [saveError, setSaveError] = useState(null);
    const [uploadingField, setUploadingField] = useState(null);
    const [showAdvanced, setShowAdvanced] = useState(false);
    const baselineRef = useRef(snapshot(DEFAULT_FORM, DEFAULT_BRAND_FORM));
    const siteTitleRef = useRef(null);
    // Config-managed source detection (admin branding configured in actuate.config.ts).
    const branding = config?.admin?.branding;
    const sources = useMemo(() => {
        const out = {};
        if (branding?.primaryColor) {
            out.accent = {
                source: 'config',
                editable: false,
                reason: 'Primary color is set in actuate.config.ts (admin.branding.primaryColor).',
            };
        }
        if (branding?.darkMode !== undefined) {
            out.colorMode = {
                source: 'config',
                editable: true,
                reason: 'Default dark mode comes from admin.branding.darkMode; you can still override it.',
            };
        }
        return out;
    }, [branding]);
    const pushLive = useCallback((a, b, assetMap) => {
        applyAppearance({
            theme: a.colorMode,
            accent: a.accent,
            density: a.density,
            font: a.font,
            reduceMotion: a.reduceMotion,
            adminBrand: assetToCache(b, assetMap, siteTitleRef.current),
        });
    }, [applyAppearance]);
    const load = useCallback(async () => {
        setLoading(true);
        setLoadError(null);
        const res = await cmsApi('/appearance');
        if (res.error || !res.data) {
            setLoadError(res.error ?? 'Failed to load appearance settings');
            setLoading(false);
            return;
        }
        const d = res.data;
        const nextAppearance = {
            colorMode: d.appearance.adminColorModeDefault,
            accent: d.appearance.adminAccentColor,
            density: d.appearance.sidebarDensity,
            font: d.appearance.contentFont,
            reduceMotion: d.appearance.reduceMotion,
        };
        const nextBrand = { ...d.brand, mode: d.brand?.mode ?? 'inheritPublicBrand' };
        const nextAssets = {};
        for (const a of d.assets ?? []) {
            nextAssets[a.id] = {
                id: a.id,
                url: a.publicUrl,
                mimeType: a.mimeType,
                width: a.width ?? undefined,
                height: a.height ?? undefined,
                shape: classifyLogoShape(a.width ?? undefined, a.height ?? undefined),
            };
        }
        siteTitleRef.current = d.siteTitle ?? null;
        setAppearance(nextAppearance);
        setBrand(nextBrand);
        setAssets(nextAssets);
        if (nextBrand.adminLogoMarkAssetId || nextBrand.publicLogoMarkAssetId)
            setShowAdvanced(true);
        baselineRef.current = snapshot(nextAppearance, nextBrand);
        pushLive(nextAppearance, nextBrand, nextAssets);
        setSaveState('idle');
        setLoading(false);
    }, [pushLive]);
    useEffect(() => {
        void load();
    }, [load]);
    // ── Mutators (live-apply theme controls before save) ──────────────────────
    const update = useCallback((next, b, assetMap) => {
        setAppearance(next);
        setBrand(b);
        setAssets(assetMap);
        setSaveState('idle');
        pushLive(next, b, assetMap);
    }, [pushLive]);
    const setColorMode = useCallback((v) => update({ ...appearance, colorMode: v }, brand, assets), [appearance, brand, assets, update]);
    const setAccent = useCallback((v) => update({ ...appearance, accent: v }, brand, assets), [appearance, brand, assets, update]);
    const setDensity = useCallback((v) => update({ ...appearance, density: v }, brand, assets), [appearance, brand, assets, update]);
    const setFont = useCallback((v) => update({ ...appearance, font: v }, brand, assets), [appearance, brand, assets, update]);
    const setReduceMotion = useCallback((v) => update({ ...appearance, reduceMotion: v }, brand, assets), [appearance, brand, assets, update]);
    const setBrandingMode = useCallback((v) => update(appearance, { ...brand, mode: v }, assets), [appearance, brand, assets, update]);
    const setAltText = useCallback((v) => update(appearance, { ...brand, defaultLogoAltText: v }, assets), [appearance, brand, assets, update]);
    const uploadAsset = useCallback(async (field, file) => {
        const isFavicon = field === 'faviconSourceAssetId';
        const allowed = isFavicon ? FAVICON_MIME : LOGO_MIME;
        if (!allowed.includes(file.type)) {
            setSaveError(`Unsupported file type. Allowed: ${allowed.join(', ')}.`);
            return;
        }
        if (file.size > MAX_BYTES) {
            setSaveError('File exceeds the 1MB limit.');
            return;
        }
        setSaveError(null);
        setUploadingField(field);
        try {
            const shape = await detectShape(file);
            const form = new FormData();
            form.append('file', file);
            // Preserve original format (SVG/PNG/ICO fidelity) — brand assets must
            // not be transcoded to WebP.
            form.append('skipOptimize', 'true');
            const res = await cmsApi('/media/upload', {
                method: 'POST',
                body: form,
            });
            if (res.error || !res.data?.id) {
                setSaveError(res.error ?? 'Upload failed');
                return;
            }
            const id = res.data.id;
            const nextAssets = {
                ...assets,
                [id]: {
                    id,
                    url: res.data.url ?? '',
                    mimeType: res.data.mimeType ?? file.type,
                    shape,
                },
            };
            update(appearance, { ...brand, [field]: id }, nextAssets);
        }
        finally {
            setUploadingField(null);
        }
    }, [appearance, brand, assets, update]);
    const selectAsset = useCallback((field, item) => {
        // Same allow-list as upload: brand assets must be SVG or PNG, regardless
        // of what else lives in the media library.
        const isFavicon = field === 'faviconSourceAssetId';
        const allowed = isFavicon ? FAVICON_MIME : LOGO_MIME;
        if (!allowed.includes(item.mimeType)) {
            setSaveError(`Unsupported file type. Brand assets must be ${allowed.join(' or ')}.`);
            return;
        }
        setSaveError(null);
        const nextAssets = {
            ...assets,
            [item.id]: {
                id: item.id,
                url: item.url,
                mimeType: item.mimeType,
                width: item.width,
                height: item.height,
                shape: classifyLogoShape(item.width, item.height),
            },
        };
        update(appearance, { ...brand, [field]: item.id }, nextAssets);
    }, [appearance, brand, assets, update]);
    const removeAsset = useCallback((field) => {
        const next = { ...brand };
        delete next[field];
        update(appearance, next, assets);
    }, [appearance, brand, assets, update]);
    const issues = useMemo(() => {
        const out = [];
        const accentCheck = validateAccentColor(appearance.accent);
        if (!accentCheck.valid) {
            out.push({
                field: 'accent',
                message: accentCheck.reason ?? 'Invalid accent color.',
                severity: 'error',
            });
        }
        // Shape warnings per asset field.
        for (const field of Object.keys(FIELD_ROLE)) {
            const id = brand[field];
            if (!id)
                continue;
            const shape = assets[id]?.shape;
            if (!shape)
                continue;
            const w = logoShapeWarning(FIELD_ROLE[field], shape);
            if (w)
                out.push({ field, message: w, severity: 'warning' });
        }
        // Dark mode default but no dark logo.
        const hasDark = Boolean(brand.publicDarkLogoAssetId || brand.adminDarkLogoAssetId);
        if (appearance.colorMode === 'dark' && !hasDark) {
            out.push({
                field: 'colorMode',
                message: 'Dark mode is the default but no dark-mode logo is set. The primary logo will be used on dark backgrounds.',
                severity: 'warning',
            });
        }
        return out;
    }, [appearance, brand, assets]);
    const preview = useMemo(() => {
        const map = new Map(Object.values(assets).map((a) => [
            a.id,
            { id: a.id, publicUrl: a.url, mimeType: a.mimeType, width: a.width, height: a.height },
        ]));
        const title = siteTitleRef.current;
        const faviconId = brand.faviconSourceAssetId;
        return {
            admin: resolveAdminBrand(brand, map, title),
            public: resolvePublicBrand(brand, map, title),
            faviconUrl: faviconId ? (assets[faviconId]?.url ?? null) : null,
        };
    }, [brand, assets]);
    const dirty = useMemo(() => snapshot(appearance, brand) !== baselineRef.current, [appearance, brand]);
    const save = useCallback(async () => {
        if (!canEdit)
            return;
        if (issues.some((i) => i.severity === 'error'))
            return;
        setSaveState('saving');
        setSaveError(null);
        // Read-modify-write the settings global so we never clobber sibling keys
        // (siteTitle, SEO defaults, AI flags, …) written by other settings tabs.
        const current = await cmsApi('/globals/settings');
        if (current.error) {
            setSaveState('error');
            setSaveError(current.error);
            return;
        }
        const now = new Date().toISOString();
        const brandPayload = { ...brand, updatedAt: now };
        const appearancePayload = {
            adminColorModeDefault: appearance.colorMode,
            adminAccentColor: appearance.accent,
            sidebarDensity: appearance.density,
            contentFont: appearance.font,
            reduceMotion: appearance.reduceMotion,
            updatedAt: now,
        };
        const merged = {
            ...(current.data ?? {}),
            appearance: appearancePayload,
            brand: brandPayload,
        };
        const res = await cmsApi('/globals/settings', { method: 'PUT', body: JSON.stringify(merged) });
        if (res.error) {
            setSaveState('error');
            setSaveError(res.error);
            return;
        }
        baselineRef.current = snapshot(appearance, brand);
        setAdminBrand(assetToCache(brand, assets, siteTitleRef.current));
        setSaveState('success');
    }, [canEdit, issues, appearance, brand, assets, setAdminBrand]);
    const reset = useCallback(() => {
        const base = JSON.parse(baselineRef.current);
        update(base.a, base.b, assets);
    }, [assets, update]);
    return {
        loading,
        loadError,
        appearance,
        brand,
        assets,
        sources,
        issues,
        preview,
        dirty,
        saveState,
        saveError,
        uploadingField,
        showAdvanced,
        setShowAdvanced,
        setColorMode,
        setAccent,
        setDensity,
        setFont,
        setReduceMotion,
        setBrandingMode,
        setAltText,
        uploadAsset,
        selectAsset,
        removeAsset,
        save,
        reset,
        reload: () => void load(),
    };
}
//# sourceMappingURL=useAppearanceSettings.js.map