<?php

// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedNamespaceFound -- APD is the established namespace shared with the Pro add-on and retained for backward compatibility.
namespace APD\Admin;

use APD\Support\AssetCatalog;
use APD\Support\RequestData;
use APD\Support\SvgSanitizer;

defined('ABSPATH') || exit;

class CatalogCrudController
{
    private function assertCapability() : void
    {
        if (! current_user_can('manage_asuka_designer')) {
            wp_die(esc_html__('You are not allowed to manage this catalog.', 'asuka-product-designer'));
        }
    }

    private function redirect(string $page, string $notice, array $extra = []) : never
    {
        wp_safe_redirect(add_query_arg(array_merge([
            'page' => $page,
            'apd_notice' => $notice,
        ], $extra), admin_url('admin.php')));
        exit;
    }


    public function handleQueryAction() : void
    {
        if (! is_admin()) {
            return;
        }

        $query = RequestData::query();
        $action = sanitize_key((string) ($query['apd_catalog_do'] ?? ''));
        if (! in_array($action, ['duplicate', 'delete', 'move'], true)) {
            return;
        }

        $page = sanitize_key((string) ($query['page'] ?? ''));
        $collection = sanitize_key((string) ($query['apd_collection'] ?? ''));
        $index = isset($query['apd_index']) ? max(0, (int) $query['apd_index']) : -1;
        $direction = sanitize_key((string) ($query['apd_direction'] ?? 'up'));
        if (! in_array($direction, ['up', 'down'], true)) {
            $direction = 'up';
        }

        if ($page === '' || $collection === '' || $index < 0) {
            return;
        }

        $this->assertCapability();

        $nonceAction = 'apd_catalog_action_' . $action . '_' . $collection . '_' . $index;
        if ($action === 'move') {
            $nonceAction .= '_' . $direction;
        }
        check_admin_referer($nonceAction);

        $catalog = new AssetCatalog();
        $notice = 'saved';
        if ($action === 'duplicate') {
            $catalog->duplicateItem($collection, $index);
            $notice = 'duplicated';
        } elseif ($action === 'delete') {
            $catalog->deleteItem($collection, $index);
            $notice = 'deleted';
        } elseif ($action === 'move') {
            $catalog->moveItem($collection, $index, $direction);
            $notice = 'reordered';
        }

        $this->redirect($page, $notice, ['apd_collection' => $collection]);
    }

    public function save() : void
    {
        $this->assertCapability();
        check_admin_referer('apd_save_catalog_item');

        $post = RequestData::post();
        $page = sanitize_key((string) ($post['apd_page'] ?? 'apd-designs'));
        $collection = sanitize_key((string) ($post['apd_collection'] ?? 'designs'));
        $indexRaw = isset($post['apd_index']) ? sanitize_text_field((string) $post['apd_index']) : '';
        $index = $indexRaw !== '' ? max(0, (int) $indexRaw) : null;
        $item = isset($post['item']) && is_array($post['item']) ? $post['item'] : [];

        $catalog = new AssetCatalog();
        $previousItem = $index !== null ? $catalog->getItem($collection, $index) : null;
        $savedIndex = $catalog->saveItem($collection, $item, $index);
        if ($collection === 'colors') {
            $previousId = is_array($previousItem) ? sanitize_key((string) ($previousItem['id'] ?? '')) : '';
            $savedItem = $catalog->getItem('colors', $savedIndex);
            $savedId = is_array($savedItem) ? sanitize_key((string) ($savedItem['id'] ?? '')) : '';
            if ($previousId !== '' && $previousId !== $savedId) {
                $this->removeColorRelations($catalog, $previousId);
            }
            $this->syncSavedColorRelations($catalog, $savedIndex);
        } elseif ($collection === 'printing_methods') {
            $previousId = is_array($previousItem) ? sanitize_key((string) ($previousItem['id'] ?? '')) : '';
            $savedItem = $catalog->getItem('printing_methods', $savedIndex);
            $savedId = is_array($savedItem) ? sanitize_key((string) ($savedItem['id'] ?? '')) : '';
            if ($previousId !== '' && $previousId !== $savedId) {
                $this->removePrintingMethodRelations($catalog, $previousId);
            }
            $this->syncSavedPrintingMethodRelations($catalog, $savedIndex);
        }

        $this->redirect($page, 'saved', ['apd_collection' => $collection]);
    }

    public function delete() : void
    {
        $this->assertCapability();
        check_admin_referer('apd_delete_catalog_item');

        $post = RequestData::post();
        $page = sanitize_key((string) ($post['apd_page'] ?? 'apd-designs'));
        $collection = sanitize_key((string) ($post['apd_collection'] ?? 'designs'));
        $index = max(0, (int) ($post['apd_index'] ?? 0));

        $catalog = new AssetCatalog();
        $deletedItem = $catalog->getItem($collection, $index);
        $catalog->deleteItem($collection, $index);
        if (is_array($deletedItem) && $collection === 'colors') {
            $this->removeColorRelations($catalog, sanitize_key((string) ($deletedItem['id'] ?? '')));
        } elseif (is_array($deletedItem) && $collection === 'printing_methods') {
            $this->removePrintingMethodRelations($catalog, sanitize_key((string) ($deletedItem['id'] ?? '')));
        }

        $this->redirect($page, 'deleted', ['apd_collection' => $collection]);
    }

    public function duplicate() : void
    {
        $this->assertCapability();
        check_admin_referer('apd_duplicate_catalog_item');

        $post = RequestData::post();
        $page = sanitize_key((string) ($post['apd_page'] ?? 'apd-designs'));
        $collection = sanitize_key((string) ($post['apd_collection'] ?? 'designs'));
        $index = max(0, (int) ($post['apd_index'] ?? 0));

        $catalog = new AssetCatalog();
        $duplicatedIndex = $catalog->duplicateItem($collection, $index);
        if ($duplicatedIndex !== null && $collection === 'colors') {
            $this->syncSavedColorRelations($catalog, $duplicatedIndex);
        } elseif ($duplicatedIndex !== null && $collection === 'printing_methods') {
            $this->syncSavedPrintingMethodRelations($catalog, $duplicatedIndex);
        }

        $this->redirect($page, 'duplicated', ['apd_collection' => $collection]);
    }

    public function move() : void
    {
        $this->assertCapability();
        check_admin_referer('apd_move_catalog_item');

        $post = RequestData::post();
        $page = sanitize_key((string) ($post['apd_page'] ?? 'apd-designs'));
        $collection = sanitize_key((string) ($post['apd_collection'] ?? 'designs'));
        $index = max(0, (int) ($post['apd_index'] ?? 0));
        $direction = sanitize_key((string) ($post['apd_direction'] ?? 'up'));
        if (! in_array($direction, ['up', 'down'], true)) {
            $direction = 'up';
        }

        (new AssetCatalog())->moveItem($collection, $index, $direction);

        $this->redirect($page, 'reordered', ['apd_collection' => $collection]);
    }

    private function syncSavedColorRelations(AssetCatalog $catalog, int $savedIndex) : void
    {
        $color = $catalog->getItem('colors', $savedIndex);
        if (! is_array($color)) {
            return;
        }

        $colorId = sanitize_key((string) ($color['id'] ?? ''));
        if ($colorId === '') {
            return;
        }

        $colors = $catalog->getCollection('colors');
        $allColorIds = $this->itemIds($colors);
        $mode = $this->relationMode($color['printingMethodMode'] ?? '', $color['printingMethodIds'] ?? []);
        $allowedMethodIds = $this->idList($color['printingMethodIds'] ?? []);
        $methods = $catalog->getCollection('printing_methods');

        foreach ($methods as &$method) {
            if (! is_array($method)) {
                continue;
            }
            $methodId = sanitize_key((string) ($method['id'] ?? ''));
            if ($methodId === '') {
                continue;
            }

            $paletteMode = $this->relationMode($method['palette']['mode'] ?? '', $method['colorIds'] ?? []);
            $colorIds = $paletteMode === 'all'
                ? $allColorIds
                : ($paletteMode === 'none' ? [] : $this->idList($method['colorIds'] ?? []));
            $allowsColor = $mode === 'all' || ($mode === 'restricted' && in_array($methodId, $allowedMethodIds, true));

            if ($allowsColor) {
                $colorIds[] = $colorId;
            } else {
                $colorIds = array_values(array_diff($colorIds, [$colorId]));
            }

            [$paletteMode, $colorIds] = $this->normalizeExplicitRelation($colorIds, $allColorIds);
            $method['colorIds'] = $colorIds;
            $method['palette'] = [
                'mode' => $paletteMode,
                'colorIds' => $colorIds,
            ];
        }
        unset($method);

        $catalog->replaceCollection('printing_methods', $methods);
    }

    private function syncSavedPrintingMethodRelations(AssetCatalog $catalog, int $savedIndex) : void
    {
        $method = $catalog->getItem('printing_methods', $savedIndex);
        if (! is_array($method)) {
            return;
        }

        $methodId = sanitize_key((string) ($method['id'] ?? ''));
        if ($methodId === '') {
            return;
        }

        $methods = $catalog->getCollection('printing_methods');
        $allMethodIds = $this->itemIds($methods);
        $paletteMode = $this->relationMode($method['palette']['mode'] ?? '', $method['colorIds'] ?? []);
        $allowedColorIds = $this->idList($method['colorIds'] ?? []);
        $colors = $catalog->getCollection('colors');

        foreach ($colors as &$color) {
            if (! is_array($color)) {
                continue;
            }
            $colorId = sanitize_key((string) ($color['id'] ?? ''));
            if ($colorId === '') {
                continue;
            }

            $colorMode = $this->relationMode($color['printingMethodMode'] ?? '', $color['printingMethodIds'] ?? []);
            $methodIds = $colorMode === 'all'
                ? $allMethodIds
                : ($colorMode === 'none' ? [] : $this->idList($color['printingMethodIds'] ?? []));
            $allowsColor = $paletteMode === 'all' || ($paletteMode === 'restricted' && in_array($colorId, $allowedColorIds, true));

            if ($allowsColor) {
                $methodIds[] = $methodId;
            } else {
                $methodIds = array_values(array_diff($methodIds, [$methodId]));
            }

            [$colorMode, $methodIds] = $this->normalizeExplicitRelation($methodIds, $allMethodIds);
            $color['printingMethodMode'] = $colorMode;
            $color['printingMethodIds'] = $methodIds;
        }
        unset($color);

        $catalog->replaceCollection('colors', $colors);
    }

    private function removeColorRelations(AssetCatalog $catalog, string $colorId) : void
    {
        if ($colorId === '') {
            return;
        }

        $allColorIds = $this->itemIds($catalog->getCollection('colors'));
        $methods = $catalog->getCollection('printing_methods');
        foreach ($methods as &$method) {
            if (! is_array($method)) {
                continue;
            }
            $mode = $this->relationMode($method['palette']['mode'] ?? '', $method['colorIds'] ?? []);
            if ($mode !== 'restricted') {
                continue;
            }
            [$mode, $colorIds] = $this->normalizeExplicitRelation(
                array_values(array_diff($this->idList($method['colorIds'] ?? []), [$colorId])),
                $allColorIds
            );
            $method['colorIds'] = $colorIds;
            $method['palette'] = ['mode' => $mode, 'colorIds' => $colorIds];
        }
        unset($method);
        $catalog->replaceCollection('printing_methods', $methods);
    }

    private function removePrintingMethodRelations(AssetCatalog $catalog, string $methodId) : void
    {
        if ($methodId === '') {
            return;
        }

        $allMethodIds = $this->itemIds($catalog->getCollection('printing_methods'));
        $colors = $catalog->getCollection('colors');
        foreach ($colors as &$color) {
            if (! is_array($color)) {
                continue;
            }
            $mode = $this->relationMode($color['printingMethodMode'] ?? '', $color['printingMethodIds'] ?? []);
            if ($mode !== 'restricted') {
                continue;
            }
            [$mode, $methodIds] = $this->normalizeExplicitRelation(
                array_values(array_diff($this->idList($color['printingMethodIds'] ?? []), [$methodId])),
                $allMethodIds
            );
            $color['printingMethodMode'] = $mode;
            $color['printingMethodIds'] = $methodIds;
        }
        unset($color);
        $catalog->replaceCollection('colors', $colors);
    }

    private function relationMode(mixed $mode, mixed $ids) : string
    {
        $mode = sanitize_key((string) $mode);
        if (in_array($mode, ['all', 'restricted', 'none'], true)) {
            return $mode;
        }
        return $this->idList($ids) !== [] ? 'restricted' : 'all';
    }

    private function normalizeExplicitRelation(array $selectedIds, array $allIds) : array
    {
        $allIds = array_values(array_unique($this->idList($allIds)));
        $selectedIds = array_values(array_intersect($allIds, array_unique($this->idList($selectedIds))));
        if ($allIds === [] || $selectedIds === $allIds) {
            return ['all', []];
        }
        if ($selectedIds === []) {
            return ['none', []];
        }
        return ['restricted', $selectedIds];
    }

    private function itemIds(array $items) : array
    {
        $ids = [];
        foreach ($items as $item) {
            if (! is_array($item)) {
                continue;
            }
            $id = sanitize_key((string) ($item['id'] ?? ''));
            if ($id !== '') {
                $ids[] = $id;
            }
        }
        return array_values(array_unique($ids));
    }

    private function idList(mixed $value) : array
    {
        if (is_string($value)) {
            $value = preg_split('/[\s,]+/', $value) ?: [];
        }
        if (! is_array($value)) {
            return [];
        }
        return array_values(array_unique(array_filter(array_map(
            static fn($id) => sanitize_key((string) $id),
            $value
        ))));
    }

    public function import() : void
    {
        $this->assertCapability();
        check_admin_referer('apd_import_catalog_items');

        $post = RequestData::post();
        $files = RequestData::files();
        $page = sanitize_key((string) ($post['apd_page'] ?? 'apd-designs'));
        $collection = sanitize_key((string) ($post['apd_collection'] ?? 'designs'));
        $mode = sanitize_key((string) ($post['apd_import_mode'] ?? 'append'));
        if (! in_array($mode, ['append', 'replace'], true)) {
            $mode = 'append';
        }

        $raw = (string) ($post['apd_import_json'] ?? '');
        if ($raw === '' && ! empty($files['apd_import_file']['tmp_name'])) {
            $tmp = (string) $files['apd_import_file']['tmp_name'];
            if (is_uploaded_file($tmp) || file_exists($tmp)) {
                $raw = (string) file_get_contents($tmp);
            }
        }

        $decoded = json_decode($raw, true);
        if (is_array($decoded) && isset($decoded[$collection]) && is_array($decoded[$collection])) {
            $decoded = $decoded[$collection];
        }

        if (! is_array($decoded)) {
            $this->redirect($page, 'import_error', [
                'apd_collection' => $collection,
                'apd_action' => 'import',
            ]);
        }

        $catalog = new AssetCatalog();
        if ($mode === 'replace') {
            $catalog->replaceCollection($collection, array_values($decoded));
        } else {
            $catalog->appendItems($collection, array_values($decoded));
        }

        $this->redirect($page, 'imported', ['apd_collection' => $collection]);
    }


    public function importMedia() : void
    {
        $this->assertCapability();
        check_admin_referer('apd_import_catalog_media');

        $post = RequestData::post();
        $page = sanitize_key((string) ($post['apd_page'] ?? 'apd-designs'));
        $collection = sanitize_key((string) ($post['apd_collection'] ?? 'designs'));
        $mode = sanitize_key((string) ($post['apd_import_mode'] ?? 'append'));
        if (! in_array($mode, ['append', 'replace'], true)) {
            $mode = 'append';
        }

        $ids = preg_split('/[\s,]+/', (string) ($post['apd_media_ids'] ?? ''));
        if (! is_array($ids)) {
            $ids = [];
        }
        $attachmentIds = array_values(array_filter(array_map('absint', $ids)));
        if ($attachmentIds === []) {
            $this->redirect($page, 'import_error', [
                'apd_collection' => $collection,
                'apd_action' => 'media-import',
            ]);
        }

        $catalog = new AssetCatalog();
        $items = [];
        foreach ($attachmentIds as $attachmentId) {
            $item = $this->buildCatalogItemFromAttachment($catalog, $collection, $attachmentId);
            if (is_array($item)) {
                $items[] = $item;
            }
        }

        if ($items === []) {
            $this->redirect($page, 'import_error', [
                'apd_collection' => $collection,
                'apd_action' => 'media-import',
            ]);
        }

        if ($mode === 'replace') {
            $catalog->replaceCollection($collection, $items);
        } else {
            $catalog->appendItems($collection, $items);
        }

        $this->redirect($page, 'imported_media', ['apd_collection' => $collection]);
    }

    private function buildCatalogItemFromAttachment(AssetCatalog $catalog, string $collection, int $attachmentId) : ?array
    {
        if (! in_array($collection, ['designs', 'shapes'], true)) {
            return null;
        }

        $attachment = get_post($attachmentId);
        if (! $attachment || $attachment->post_type !== 'attachment') {
            return null;
        }

        $url = wp_get_attachment_url($attachmentId);
        if (! is_string($url) || $url === '') {
            return null;
        }

        $title = trim((string) get_the_title($attachmentId));
        if ($title === '') {
            $title = preg_replace('/\.[^.]+$/', '', basename((string) get_attached_file($attachmentId))) ?: sprintf(/* translators: Placeholder values are populated from the current administration record. */ __('Media %d', 'asuka-product-designer'), $attachmentId);
        }
        $mime = (string) get_post_mime_type($attachmentId);
        $ext = strtolower((string) pathinfo(wp_parse_url($url, PHP_URL_PATH) ?: '', PATHINFO_EXTENSION));
        $isSvg = $mime === 'image/svg+xml' || $ext === 'svg';
        $description = trim((string) wp_strip_all_tags((string) ($attachment->post_excerpt ?? '')));
        $baseId = sanitize_title($title);
        if ($baseId === '') {
            $baseId = 'media-' . $attachmentId;
        }

        if ($collection === 'designs') {
            return [
                'schemaVersion' => \APD\Support\RuntimeDataSchema::VERSION,
                'id' => $catalog->nextUniqueId('designs', $baseId, $title),
                'label' => $title,
                'name' => $title,
                'itemType' => 'design',
                'category' => '',
                'description' => $description,
                'url' => $url,
                'sourceUrl' => $url,
                'previewUrl' => $url,
                'thumbnail' => $url,
                'price' => 0,
                'enabled' => true,
            ];
        }

        if (! $isSvg) {
            return null;
        }

        $svgText = '';
        $file = (string) get_attached_file($attachmentId);
        if ($file !== '' && is_readable($file)) {
            $svgText = SvgSanitizer::sanitize((string) file_get_contents($file));
        }
        if ($svgText === '') {
            return null;
        }

        return [
            'schemaVersion' => \APD\Support\RuntimeDataSchema::VERSION,
            'id' => $catalog->nextUniqueId('shapes', $baseId, $title),
            'label' => $title,
            'name' => $title,
            'itemType' => 'shape',
            'category' => '',
            'type' => 'svg',
            'viewBox' => '0 0 100 100',
            'fill' => 'currentColor',
            'stroke' => '',
            'price' => 0,
            'sourceUrl' => $url,
            'svgText' => $svgText,
            'enabled' => true,
        ];
    }

    public function export() : void
    {
        $this->assertCapability();
        check_admin_referer('apd_export_catalog_items');

        $request = RequestData::request();
        $collection = sanitize_key((string) ($request['apd_collection'] ?? 'designs'));
        $payload = [
            'exportedAt' => gmdate('c'),
            'collection' => $collection,
            $collection => (new AssetCatalog())->getCollection($collection),
        ];

        nocache_headers();
        header('Content-Type: application/json; charset=utf-8');
        header('Content-Disposition: attachment; filename="apd-' . $collection . '-' . gmdate('Ymd-His') . ' .json"');
        echo wp_json_encode($payload, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
        exit;
    }
}
