<?php
/**
 * Bibliothèque de seed Covalba — convertit du JSON "sémantique" (noms de champs
 * snake_case du SCHEMA.md) en valeurs ACF écrites par field keys.
 *
 * Les maps nom→key sont lues depuis les définitions ACF réellement enregistrées
 * (mu-plugin covalba-core), donc robustes aux renommages type items_certifications.
 *
 * Format JSON d'un document :
 * {
 *   "type": "page|produit|toiture|industrie|reference|lieu|options",
 *   "slug": "...", "title": "...", "status": "publish",
 *   "parent": "slug-parent" (optionnel, pages),
 *   "seo": { "titre_seo": ..., "meta_description": ..., ... },
 *   "fiche_produit": {...}, "fiche_reference": {...},
 *   "secteurs": ["logistique"], "types_support": ["bac-acier"],
 *   "sections": [ { "layout": "hero", "titre": "...", "image": {"path": "...", "alt": "..."}, ... } ]
 * }
 *
 * Images locales : { "path": "images/toitures/x.webp", "alt": "..." } — path relatif à
 * CVBSEED_ASSETS_DIR (les assets sont rsyncés sur le VPS avant le seed).
 * Images distantes : { "url": "https://...", "alt": "...", "credit": "...", "source": "..." }.
 */

defined('ABSPATH') || exit;

const CVBSEED_ASSETS_DIR = '/var/www/html/wp-content/seed-assets';

/** Upload (ou retrouve) un média par chemin source. Dédupe via meta _covalba_source_path. */
function cvbseed_media(string $path, string $alt = ''): int {
    $path = ltrim($path, '/');
    $existing = get_posts([
        'post_type' => 'attachment', 'posts_per_page' => 1, 'fields' => 'ids',
        'meta_key' => '_covalba_source_path', 'meta_value' => $path,
    ]);
    if ($existing) {
        if ($alt) update_post_meta($existing[0], '_wp_attachment_image_alt', $alt);
        return (int) $existing[0];
    }
    $abs = CVBSEED_ASSETS_DIR . '/' . $path;
    if (!file_exists($abs)) {
        WP_CLI::warning("asset introuvable : $path");
        return 0;
    }
    // Copie temporaire : media_handle_sideload déplace le fichier
    $tmp = wp_tempnam(basename($abs));
    copy($abs, $tmp);
    require_once ABSPATH . 'wp-admin/includes/image.php';
    require_once ABSPATH . 'wp-admin/includes/file.php';
    require_once ABSPATH . 'wp-admin/includes/media.php';
    $id = media_handle_sideload(['name' => basename($abs), 'tmp_name' => $tmp], 0);
    if (is_wp_error($id)) {
        WP_CLI::warning("upload échoué $path : " . $id->get_error_message());
        return 0;
    }
    update_post_meta($id, '_covalba_source_path', $path);
    if ($alt) update_post_meta($id, '_wp_attachment_image_alt', $alt);
    return (int) $id;
}

/** Upload (ou retrouve) un média distant par URL source. */
function cvbseed_remote_media(string $url, string $alt = '', string $credit = '', string $source = ''): int {
    $url = trim($url);
    if (!$url) return 0;

    $existing = get_posts([
        'post_type' => 'attachment', 'posts_per_page' => 1, 'fields' => 'ids',
        'meta_key' => '_covalba_source_url', 'meta_value' => $url,
    ]);
    if ($existing) {
        if ($alt) update_post_meta($existing[0], '_wp_attachment_image_alt', $alt);
        if ($credit) update_post_meta($existing[0], '_covalba_image_credit', $credit);
        if ($source) update_post_meta($existing[0], '_covalba_image_source', $source);
        return (int) $existing[0];
    }

    require_once ABSPATH . 'wp-admin/includes/image.php';
    require_once ABSPATH . 'wp-admin/includes/file.php';
    require_once ABSPATH . 'wp-admin/includes/media.php';

    $tmp = download_url($url, 30);
    if (is_wp_error($tmp)) {
        WP_CLI::warning("téléchargement image échoué $url : " . $tmp->get_error_message());
        return 0;
    }

    $basename = basename((string) parse_url($url, PHP_URL_PATH));
    if (!$basename || !preg_match('/\.(webp|png|jpe?g|gif|avif)$/i', $basename)) {
        $basename = 'covalba-lieu-' . substr(md5($url), 0, 12) . '.jpg';
    }

    $id = media_handle_sideload(['name' => $basename, 'tmp_name' => $tmp], 0);
    if (is_wp_error($id)) {
        @unlink($tmp);
        WP_CLI::warning("upload image distante échoué $url : " . $id->get_error_message());
        return 0;
    }

    update_post_meta($id, '_covalba_source_url', $url);
    if ($credit) update_post_meta($id, '_covalba_image_credit', $credit);
    if ($source) update_post_meta($id, '_covalba_image_source', $source);
    if ($alt) update_post_meta($id, '_wp_attachment_image_alt', $alt);
    return (int) $id;
}

/** name → définition de champ, pour une liste de sub_fields ACF. */
function cvbseed_index_fields(array $fields): array {
    $map = [];
    foreach ($fields as $f) {
        if (!empty($f['name'])) $map[$f['name']] = $f;
    }
    return $map;
}

/** Convertit une valeur sémantique selon la définition ACF du champ. */
function cvbseed_value(array $field, $value) {
    switch ($field['type']) {
        case 'image':
        case 'file':
            if (is_array($value) && isset($value['url'])) {
                return cvbseed_remote_media(
                    $value['url'],
                    $value['alt'] ?? '',
                    $value['credit'] ?? '',
                    $value['source'] ?? ''
                );
            }
            if (is_array($value) && isset($value['path'])) {
                return cvbseed_media($value['path'], $value['alt'] ?? '');
            }
            return is_numeric($value) ? (int) $value : 0;
        case 'repeater':
            $sub = cvbseed_index_fields($field['sub_fields'] ?? []);
            $rows = [];
            foreach ((array) $value as $row) {
                $r = [];
                foreach ((array) $row as $name => $v) {
                    if (!isset($sub[$name])) { WP_CLI::warning("sous-champ inconnu '{$name}' dans {$field['name']}"); continue; }
                    $r[$sub[$name]['key']] = cvbseed_value($sub[$name], $v);
                }
                $rows[] = $r;
            }
            return $rows;
        case 'group':
            $sub = cvbseed_index_fields($field['sub_fields'] ?? []);
            $g = [];
            foreach ((array) $value as $name => $v) {
                if (!isset($sub[$name])) { WP_CLI::warning("sous-champ inconnu '{$name}' dans {$field['name']}"); continue; }
                $g[$sub[$name]['key']] = cvbseed_value($sub[$name], $v);
            }
            return $g;
        case 'true_false':
            return $value ? 1 : 0;
        case 'relationship':
        case 'post_object':
            // valeur = liste de slugs -> IDs (tous post types covalba confondus)
            $ids = [];
            foreach ((array) $value as $slug) {
                $p = cvbseed_find_post($slug);
                if ($p) $ids[] = $p;
            }
            return $ids;
        default:
            return $value;
    }
}

/** Polylang actif ? */
function cvbseed_pll(): bool { return function_exists('pll_set_post_language'); }

/** Définit la langue d'un post (idempotent, no-op si Polylang absent). */
function cvbseed_set_language(int $post_id, string $lang): void {
    if ($post_id && cvbseed_pll()) pll_set_post_language($post_id, $lang);
}

/** Lie [lang => post_id] comme traductions mutuelles (idempotent). */
function cvbseed_link_translations(array $byLang): void {
    $byLang = array_filter($byLang);
    if (cvbseed_pll() && count($byLang) > 1) pll_save_post_translations($byLang);
}

/**
 * Recherche exacte d'un post par type + langue + identité FR.
 * $frBase = slug FR canonique du groupe de traduction (= slug pour un doc FR,
 * = translation_of pour en/es). On NE s'appuie PAS sur le query-var 'lang' de
 * Polylang (non appliqué de façon fiable en contexte WP-CLI) :
 *   1) lookup déterministe par meta `_cvbseed_key` (= type|frBase|lang) ;
 *   2) fallback par slug + langue filtrée en PHP via pll_get_post_language().
 */
function cvbseed_find_post_exact(string $type, string $slug, string $lang, ?string $frBase = null): int {
    $frBase = $frBase ?? $slug;
    $byMeta = get_posts([
        'post_type' => $type, 'posts_per_page' => 1, 'fields' => 'ids', 'post_status' => 'any',
        'meta_key' => '_cvbseed_key', 'meta_value' => $type . '|' . $frBase . '|' . $lang,
    ]);
    if ($byMeta) return (int) $byMeta[0];

    $ids = get_posts(['post_type' => $type, 'name' => $slug, 'posts_per_page' => -1, 'fields' => 'ids', 'post_status' => 'any']);
    foreach ($ids as $id) {
        $pl = cvbseed_pll() ? (pll_get_post_language($id) ?: 'fr') : 'fr';
        if ($pl === $lang) return (int) $id;
    }
    return 0;
}

/**
 * Crée ou retrouve un post (type + identité FR + langue) avec le bon slug, et
 * tatoue `_cvbseed_key` pour un re-seed idempotent même si Polylang suffixe le
 * slug partagé entre langues (ex. produit "covatherm" fr/en/es, page "faq").
 */
function cvbseed_ensure_post(string $type, string $slug, string $lang, string $title, string $status, int $parent = 0, ?string $frBase = null): int {
    $frBase = $frBase ?? $slug;
    $id = cvbseed_find_post_exact($type, $slug, $lang, $frBase);
    if (!$id) {
        $id = wp_insert_post(['post_type' => $type, 'post_title' => $title, 'post_status' => $status]);
        if (is_wp_error($id) || !$id) { WP_CLI::warning("échec création $type/$slug ($lang)"); return 0; }
        cvbseed_set_language((int) $id, $lang);
        $upd = ['ID' => $id, 'post_name' => $slug];
        if ($parent) $upd['post_parent'] = $parent;
        wp_update_post($upd);
    } else {
        cvbseed_set_language($id, $lang);
    }
    update_post_meta((int) $id, '_cvbseed_key', $type . '|' . $frBase . '|' . $lang);
    return (int) $id;
}

/**
 * Résout un slug vers un ID (tous types Covalba), dans la langue courante du
 * document en cours de seed ($GLOBALS['cvbseed_lang']). Les relationships d'une
 * page EN doivent pointer vers des posts EN.
 */
function cvbseed_find_post(string $slug, ?string $lang = null): int {
    $lang = $lang ?? ($GLOBALS['cvbseed_lang'] ?? 'fr');
    // Les relations référencent toujours le slug FR canonique → frBase = $slug.
    foreach (['produit', 'toiture', 'industrie', 'reference', 'lieu', 'formulaire_hubspot', 'page'] as $type) {
        $id = cvbseed_find_post_exact($type, $slug, $lang, $slug);
        if ($id) return $id;
        if ($type === 'formulaire_hubspot' && $lang !== 'fr') {
            $id = cvbseed_find_post_exact($type, $slug, 'fr', $slug);
            if ($id) return $id;
        }
    }
    return 0;
}

/** Convertit le tableau "sections" sémantique en valeur flexible par field keys. */
function cvbseed_sections(array $sections): array {
    $flex = acf_get_field('field_cvb_sections');
    if (!$flex) { WP_CLI::error('field_cvb_sections introuvable — ACF/mu-plugin absents ?'); }
    $layouts = [];
    foreach ($flex['layouts'] as $l) $layouts[$l['name']] = cvbseed_index_fields($l['sub_fields'] ?? []);

    $rows = [];
    foreach ($sections as $i => $s) {
        $layout = $s['layout'] ?? null;
        if (!$layout || !isset($layouts[$layout])) { WP_CLI::warning("layout inconnu '#{$i}: {$layout}'"); continue; }
        $row = ['acf_fc_layout' => $layout];
        foreach ($s as $name => $v) {
            if ($name === 'layout') continue;
            $def = $layouts[$layout][$name] ?? null;
            if (!$def) { WP_CLI::warning("champ inconnu '{$name}' du layout {$layout}"); continue; }
            $row[$def['key']] = cvbseed_value($def, $v);
        }
        $rows[] = $row;
    }
    return $rows;
}

/** Écrit un groupe de champs (seo, fiche_produit, fiche_reference) par keys. */
function cvbseed_group(string $groupFieldPrefix, array $values, int $post_id, array $nameToDef): void {
    foreach ($values as $name => $v) {
        if (!isset($nameToDef[$name])) { WP_CLI::warning("champ inconnu '{$name}' ({$groupFieldPrefix})"); continue; }
        update_field($nameToDef[$name]['key'], cvbseed_value($nameToDef[$name], $v), $post_id);
    }
}

/** Map name→def des champs d'un field group ACF (par key de group). */
function cvbseed_group_fields(string $group_key): array {
    $fields = acf_get_fields($group_key) ?: [];
    return cvbseed_index_fields($fields);
}

/** Seed un document JSON. Idempotent (recherche par slug). */
function cvbseed_document(array $doc): void {
    $type = $doc['type'] ?? 'page';

    if ($type === 'options') {
        // Champs des options : update_field(key, value, 'option')
        $fields = cvbseed_group_fields('group_cvb_options');
        foreach ($doc['fields'] ?? [] as $name => $v) {
            if (!isset($fields[$name])) { WP_CLI::warning("option inconnue '{$name}'"); continue; }
            update_field($fields[$name]['key'], cvbseed_value($fields[$name], $v), 'option');
        }
        WP_CLI::success('options mises à jour');
        return;
    }

    $slug = $doc['slug'];
    $lang = $doc['lang'] ?? 'fr';
    $GLOBALS['cvbseed_lang'] = $lang;   // relations résolues dans la bonne langue

    $base = $doc['translation_of'] ?? $slug; // slug FR canonique du groupe de traduction

    $parent_id = 0;
    if (!empty($doc['parent'])) {
        // le parent doit exister dans la même langue (frBase = slug FR du parent)
        $parent_id = cvbseed_find_post_exact('page', $doc['parent'], $lang, $doc['parent']);
    }

    // Crée/retrouve le post dans sa langue avec le bon slug (gère le slug partagé Polylang)
    $post_id = cvbseed_ensure_post($type, $slug, $lang, $doc['title'] ?? $slug, $doc['status'] ?? 'publish', $parent_id, $base);
    if (!$post_id) { WP_CLI::warning("échec post $slug ($lang)"); return; }

    // Met à jour titre/statut/parent au cas où ils auraient changé
    $updateArgs = ['ID' => $post_id, 'post_title' => $doc['title'] ?? $slug, 'post_status' => $doc['status'] ?? 'publish'];
    if ($parent_id) $updateArgs['post_parent'] = $parent_id;
    wp_update_post($updateArgs);
    cvbseed_set_language($post_id, $lang);

    // Enregistre la liaison de traduction (un doc FR se référence via son propre slug)
    if (!isset($GLOBALS['cvbseed_links'])) $GLOBALS['cvbseed_links'] = [];
    $GLOBALS['cvbseed_links'][$type . '|' . $base][$lang] = $post_id;

    if (!empty($doc['thumbnail']['path'])) {
        $mid = cvbseed_media($doc['thumbnail']['path'], $doc['thumbnail']['alt'] ?? '');
        if ($mid) set_post_thumbnail($post_id, $mid);
    }
    foreach (['secteurs' => 'secteur', 'types_support' => 'type_support'] as $k => $tax) {
        if (!empty($doc[$k])) wp_set_object_terms($post_id, $doc[$k], $tax);
    }
    if (!empty($doc['seo'])) {
        cvbseed_group('seo', $doc['seo'], $post_id, cvbseed_group_fields('group_cvb_seo'));
    }
    if (!empty($doc['fiche_produit'])) {
        cvbseed_group('fiche_produit', $doc['fiche_produit'], $post_id, cvbseed_group_fields('group_cvb_fiche_produit'));
    }
    if (!empty($doc['fiche_reference'])) {
        cvbseed_group('fiche_reference', $doc['fiche_reference'], $post_id, cvbseed_group_fields('group_cvb_fiche_reference'));
    }
    if (!empty($doc['fiche_lieu'])) {
        cvbseed_group('fiche_lieu', $doc['fiche_lieu'], $post_id, cvbseed_group_fields('group_cvb_fiche_lieu'));
    }
    if (!empty($doc['fiche_formulaire_hubspot'])) {
        cvbseed_group('fiche_formulaire_hubspot', $doc['fiche_formulaire_hubspot'], $post_id, cvbseed_group_fields('group_cvb_fiche_formulaire_hubspot'));
    }
    if (isset($doc['sections'])) {
        update_field('field_cvb_sections', cvbseed_sections($doc['sections']), $post_id);
    }
    WP_CLI::success("$type/$slug → #$post_id (" . count($doc['sections'] ?? []) . ' sections)');
}
