/**
 * SEO service — typed client over the cms-core SEO REST endpoints.
 *
 * Every SEO admin component reads/writes through this module (never raw
 * `cmsApi` / `fetch`), so the network contract lives in one place. Fetchers
 * return typed data with safe fallbacks; mutations return `{ error?: string }`.
 *
 * Endpoint paths here are the contract implemented by cms-core
 * `packages/cms-core/src/api/handlers.ts` across phases 2-3 and 6.
 */
import { cmsApi } from './api.js';
/**
 * `cmsApi` never throws — it returns `{ error, status }` on a network/HTTP
 * failure. Read (GET) fetchers below must re-surface that as a thrown error so
 * `useSeoResource` can distinguish "request failed" from "empty result".
 * Without this, a failed `/seo/overview` would fall through to the empty
 * fallback (a fake score of 0 / grade "Critical") and the error/retry UI would
 * be permanently unreachable. Mutations intentionally return `{ error }` and do
 * not use this.
 */
function throwIfError(res) {
    if (res.error)
        throw new Error(res.error);
}
const EMPTY_OVERVIEW = {
    siteScore: { score: 0, grade: 'critical', delta: null, breakdown: [] },
    metrics: [],
    issues: [],
    issuesSummary: { total: 0, critical: 0, warning: 0, info: 0 },
    quickWins: [],
    searchPerformance: [],
    searchConsoleConnected: false,
    lastAuditRunId: null,
};
export async function fetchSeoOverview() {
    const res = await cmsApi('/seo/overview');
    throwIfError(res);
    return res.data ?? EMPTY_OVERVIEW;
}
export async function fetchSearchPerformance(params = {}) {
    const qs = params.limit ? `?limit=${params.limit}` : '';
    const res = await cmsApi(`/seo/search-performance${qs}`);
    throwIfError(res);
    return res.data?.rows ?? [];
}
export async function fetchSeoContentRecords() {
    const res = await cmsApi('/seo/content');
    throwIfError(res);
    return res.data?.records ?? [];
}
export async function fetchSeoContentRecord(entityType, entityId) {
    const res = await cmsApi(`/seo/content/${entityType}/${encodeURIComponent(entityId)}`);
    throwIfError(res);
    return res.data ?? null;
}
export async function updateSeoFields(entityType, entityId, payload) {
    const res = await cmsApi(`/seo/content/${entityType}/${encodeURIComponent(entityId)}`, {
        method: 'PUT',
        body: JSON.stringify(payload),
    });
    return res.error ? { error: res.error } : {};
}
/** Pure search/filter over the content records (decoupled from network for tests). */
export function applyContentSeoQuery(records, query) {
    const search = (query.search ?? '').trim().toLowerCase();
    const filter = query.filter ?? 'all';
    return records.filter((r) => {
        if (filter === 'pages' && r.entityType !== 'page')
            return false;
        if (filter === 'posts' && r.entityType !== 'post')
            return false;
        if (search) {
            const haystack = [
                r.title,
                r.url,
                r.metaTitle ?? '',
                r.metaDescription ?? '',
                String(r.seoScore),
                r.entityType,
            ]
                .join(' ')
                .toLowerCase();
            if (!haystack.includes(search))
                return false;
        }
        return true;
    });
}
export async function runSeoAudit(scope = 'full') {
    const res = await cmsApi('/seo/audit', {
        method: 'POST',
        body: JSON.stringify({ scope }),
    });
    if (res.error)
        return { error: res.error };
    return { run: res.data };
}
export async function fetchSeoAuditRuns() {
    const res = await cmsApi('/seo/audits');
    throwIfError(res);
    return res.data?.runs ?? [];
}
export async function fetchSeoAuditRun(auditRunId) {
    const res = await cmsApi(`/seo/audits/${encodeURIComponent(auditRunId)}`);
    throwIfError(res);
    return res.data ?? null;
}
export async function fetchSeoIssues(query = {}) {
    const params = new URLSearchParams();
    if (query.severity && query.severity !== 'all')
        params.set('severity', query.severity);
    if (query.category && query.category !== 'all')
        params.set('category', query.category);
    if (query.status && query.status !== 'all')
        params.set('status', query.status);
    if (query.auditRunId)
        params.set('auditRunId', query.auditRunId);
    const qs = params.toString();
    const res = await cmsApi(`/seo/issues${qs ? `?${qs}` : ''}`);
    throwIfError(res);
    return res.data?.issues ?? [];
}
export async function fetchSeoIssue(issueId) {
    const res = await cmsApi(`/seo/issues/${encodeURIComponent(issueId)}`);
    throwIfError(res);
    return res.data ?? null;
}
export async function resolveSeoIssue(issueId) {
    const res = await cmsApi(`/seo/issues/${encodeURIComponent(issueId)}/resolve`, { method: 'POST' });
    return res.error ? { error: res.error } : {};
}
export async function ignoreSeoIssue(issueId) {
    const res = await cmsApi(`/seo/issues/${encodeURIComponent(issueId)}/ignore`, { method: 'POST' });
    return res.error ? { error: res.error } : {};
}
export async function reopenSeoIssue(issueId) {
    const res = await cmsApi(`/seo/issues/${encodeURIComponent(issueId)}/reopen`, { method: 'POST' });
    return res.error ? { error: res.error } : {};
}
export async function fetchSitemapStatus() {
    const res = await cmsApi('/seo/sitemap/status');
    throwIfError(res);
    return res.data ?? null;
}
export async function regenerateSitemap() {
    const res = await cmsApi('/seo/sitemap/regenerate', { method: 'POST' });
    if (res.error)
        return { error: res.error };
    return { status: res.data };
}
export async function fetchCrawlSettings() {
    const res = await cmsApi('/seo/crawl-settings');
    throwIfError(res);
    return res.data ?? null;
}
export async function updateCrawlSettings(payload) {
    const res = await cmsApi('/seo/crawl-settings', {
        method: 'PUT',
        body: JSON.stringify(payload),
    });
    return res.error ? { error: res.error } : {};
}
export async function fetchAiCrawlerActivity() {
    const res = await cmsApi('/ai-crawlers');
    throwIfError(res);
    return res.data ?? { enabled: false, rows: [], totals: { hits: 0, bots: 0, honeypot: 0 } };
}
export async function fetchRobotsTxt() {
    const res = await cmsApi('/seo/robots');
    throwIfError(res);
    return res.data ?? null;
}
export async function updateRobotsTxt(body) {
    const res = await cmsApi('/seo/robots', { method: 'PUT', body: JSON.stringify({ body }) });
    return res.error ? { error: res.error } : {};
}
/** Client-side robots.txt validation (mirrors server-side checks). */
export function validateRobotsTxt(body) {
    const errors = [];
    const warnings = [];
    const lines = body.split(/\r?\n/);
    let blocksEntireSite = false;
    let sawUserAgent = false;
    let starAgent = false;
    for (const raw of lines) {
        const line = raw.trim();
        if (!line || line.startsWith('#'))
            continue;
        const colon = line.indexOf(':');
        if (colon === -1) {
            errors.push(`Invalid directive (missing ":"): "${line}"`);
            continue;
        }
        const directive = line.slice(0, colon).trim().toLowerCase();
        const value = line.slice(colon + 1).trim();
        if (directive === 'user-agent') {
            sawUserAgent = true;
            starAgent = value === '*';
        }
        else if (directive === 'disallow') {
            if (value === '/' && starAgent)
                blocksEntireSite = true;
        }
        else if (!['allow', 'sitemap', 'crawl-delay', 'host'].includes(directive)) {
            warnings.push(`Unknown directive: "${directive}"`);
        }
    }
    if (!sawUserAgent)
        errors.push('No User-agent directive found.');
    if (blocksEntireSite) {
        warnings.push('This configuration blocks the entire site from all crawlers.');
    }
    return { valid: errors.length === 0, blocksEntireSite, errors, warnings };
}
export async function fetchRedirectOverview() {
    const res = await cmsApi('/seo/redirects/overview');
    throwIfError(res);
    return (res.data ?? {
        activeRedirects: 0,
        staleRedirects: 0,
        notFoundCount: 0,
        notFoundHits: 0,
        chainsAndLoops: 0,
        trafficRecovered: 0,
    });
}
export async function fetchRedirectRules() {
    const res = await cmsApi('/seo/redirects');
    throwIfError(res);
    return res.data?.rules ?? [];
}
export async function createRedirectRule(payload) {
    const res = await cmsApi('/seo/redirects', { method: 'POST', body: JSON.stringify(payload) });
    return res.error ? { error: res.error } : {};
}
export async function updateRedirectRule(redirectId, payload) {
    const res = await cmsApi(`/seo/redirects/${encodeURIComponent(redirectId)}`, {
        method: 'PUT',
        body: JSON.stringify(payload),
    });
    return res.error ? { error: res.error } : {};
}
export async function deleteRedirectRule(redirectId) {
    const res = await cmsApi(`/seo/redirects/${encodeURIComponent(redirectId)}`, {
        method: 'DELETE',
    });
    return res.error ? { error: res.error } : {};
}
export async function fetchRedirectChains() {
    const res = await cmsApi('/seo/redirects/chains');
    throwIfError(res);
    return res.data?.chains ?? [];
}
export async function flattenRedirectChain(chainId) {
    const res = await cmsApi('/seo/redirects/flatten', {
        method: 'POST',
        body: JSON.stringify({ chainId }),
    });
    return res.error ? { error: res.error } : {};
}
export async function fetchRedirectSuggestions() {
    const res = await cmsApi('/seo/redirects/suggestions');
    throwIfError(res);
    return res.data?.suggestions ?? [];
}
export async function generate404RecoverySuggestions() {
    const res = await cmsApi('/seo/redirects/suggest', { method: 'POST' });
    return res.error ? { error: res.error } : {};
}
export async function acceptRedirectSuggestion(suggestionId) {
    const res = await cmsApi(`/seo/redirects/suggestions/${encodeURIComponent(suggestionId)}/accept`, {
        method: 'POST',
    });
    return res.error ? { error: res.error } : {};
}
export async function dismissRedirectSuggestion(suggestionId) {
    const res = await cmsApi(`/seo/redirects/suggestions/${encodeURIComponent(suggestionId)}/dismiss`, { method: 'POST' });
    return res.error ? { error: res.error } : {};
}
export async function exportRedirectsCsv() {
    const res = await cmsApi('/seo/redirects/export');
    if (res.error)
        return { error: res.error };
    return { csv: res.data?.csv ?? '' };
}
export async function importRedirectsCsv(csv) {
    const res = await cmsApi('/seo/redirects/import', {
        method: 'POST',
        body: JSON.stringify({ csv }),
    });
    if (res.error)
        return { error: res.error };
    return { imported: res.data?.imported ?? 0, errors: res.data?.errors ?? [] };
}
/**
 * AI SEO endpoints report unavailability with two statuses: 501 (plugin-ai not
 * installed) and 503 (installed but the provider isn't configured). Both map to
 * the same graceful "AI unavailable" UX.
 */
function isAiUnavailable(status) {
    return status === 501 || status === 503;
}
export async function generateSeoField(field, context) {
    const res = await cmsApi('/ai/seo/generate-field', {
        method: 'POST',
        body: JSON.stringify({ field, ...context }),
    });
    if (isAiUnavailable(res.status))
        return { unavailable: true };
    return res.data ?? {};
}
export async function explainSeoIssue(issueId) {
    const res = await cmsApi('/ai/seo/explain-issue', {
        method: 'POST',
        body: JSON.stringify({ issueId }),
    });
    if (isAiUnavailable(res.status))
        return { unavailable: true };
    return res.data ?? {};
}
export async function generateStructuredData(context) {
    const res = await cmsApi('/ai/seo/generate-schema', {
        method: 'POST',
        body: JSON.stringify(context),
    });
    if (isAiUnavailable(res.status))
        return { unavailable: true };
    return res.data ?? {};
}
export async function summarizeSeoAudit() {
    const res = await cmsApi('/ai/seo/summarize', { method: 'POST' });
    if (isAiUnavailable(res.status))
        return { unavailable: true };
    return res.data ?? {};
}
//# sourceMappingURL=seo-service.js.map