<?php

declare(strict_types=1);

namespace CliqThemes\StoreLocator\Repository;

defined('ABSPATH') || exit;

// This repository is the data-access layer for plugin-owned custom tables.
// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching

use CliqThemes\StoreLocator\I18n\ContentTranslator;
use CliqThemes\StoreLocator\I18n\Multilingual;
use CliqThemes\StoreLocator\Models\Brand;
use CliqThemes\StoreLocator\Support\ColumnInput;

/**
 * CRUD for brands.
 */
final class BrandRepository
{
    /**
     * @return array<int, array<string, mixed>>
     */
    public function all(): array
    {
        $query = Brand::query()->orderBy('name', 'ASC');
        ContentTranslator::scopeOriginals($query);
        $items = array_map([$this, 'normalize'], $query->raw());

        return ContentTranslator::overlay($items, 'cliq_sl_brands', ['name', 'description'], 'id');
    }

    /**
     * @return array<string, mixed>|null
     */
    public function find(int $id): ?array
    {
        $brand = Brand::find($id);

        return $brand ? $this->normalize($brand->toArray()) : null;
    }

    /**
     * @param array<string, mixed> $data
     *
     * @return array<string, mixed>
     */
    public function create(array $data): array
    {
        $data['slug'] = $this->uniqueSlug($data['slug'] ?? sanitize_title((string) ($data['name'] ?? 'brand')));
        $brand = Brand::create($this->writable($data));
        $id = (int) $brand->getKey();
        ContentTranslator::stampOriginalTrid('cliq_sl_brands', $id);

        return $this->find($id) ?? [];
    }

    /**
     * Create/update a brand's translation for one language (text = name, description).
     *
     * @param array<string, mixed> $data
     *
     * @return array<string, mixed>|null
     */
    public function saveTranslation(int $sourceId, string $lang, array $data): ?array
    {
        $lang = Multilingual::normalize($lang);
        if ($lang === '') {
            return null;
        }
        $source = Brand::find($sourceId);
        if (! $source) {
            return null;
        }

        $existing = ContentTranslator::translationsOf('cliq_sl_brands', $sourceId);
        $fields = [
            'name' => ColumnInput::sanitize('name', (string) ($data['name'] ?? $source->getAttribute('name'))),
            'description' => ColumnInput::sanitize('description', $data['description'] ?? $source->getAttribute('description')),
            'slug' => (string) $source->getAttribute('slug'),
            'lang' => $lang,
            'trid' => ContentTranslator::tridFor('cliq_sl_brands', $sourceId),
            'source_id' => $sourceId,
        ];

        if (isset($existing[$lang])) {
            $row = Brand::find($existing[$lang]);
            $row->fill($fields);
            $row->save();
            $id = $existing[$lang];
        } else {
            $row = Brand::create($fields);
            $id = (int) $row->getKey();
        }

        return ['id' => $id, 'source_id' => $sourceId] + $fields;
    }

    /**
     * @return array<string, array<string, mixed>>
     */
    public function translations(int $sourceId): array
    {
        $out = [];
        foreach (ContentTranslator::translationsOf('cliq_sl_brands', $sourceId) as $lang => $id) {
            $row = Brand::find($id);
            if ($row) {
                $out[$lang] = [
                    'id' => $id,
                    'name' => $row->getAttribute('name'),
                    'description' => $row->getAttribute('description'),
                ];
            }
        }

        return $out;
    }

    /**
     * @param array<string, mixed> $data
     *
     * @return array<string, mixed>|null
     */
    public function update(int $id, array $data): ?array
    {
        $brand = Brand::find($id);
        if (! $brand) {
            return null;
        }
        $brand->fill($this->writable($data));
        $brand->save();

        return $this->find($id);
    }

    public function delete(int $id): bool
    {
        global $wpdb;
        $wpdb->delete($wpdb->prefix . 'cliq_sl_brand_store', ['brand_id' => $id], ['%d']);
        $wpdb->delete($wpdb->prefix . 'cliq_sl_brands', ['source_id' => $id], ['%d']);

        $brand = Brand::find($id);

        return $brand ? $brand->delete() : false;
    }

    /**
     * @param array<string, mixed> $row
     *
     * @return array<string, mixed>
     */
    private function normalize(array $row): array
    {
        $row['id'] = (int) $row['id'];

        return $row;
    }

    /**
     * @param array<string, mixed> $data
     *
     * @return array<string, mixed>
     */
    private function writable(array $data): array
    {
        $out = [];
        foreach (['name', 'slug', 'website', 'description', 'logo'] as $key) {
            if (array_key_exists($key, $data)) {
                $out[$key] = ColumnInput::sanitize($key, $data[$key]);
            }
        }

        return $out;
    }

    private function uniqueSlug(string $base): string
    {
        global $wpdb;
        $base = $base !== '' ? $base : 'brand';
        $slug = $base;
        $i = 2;
        while ((int) $wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM {$wpdb->prefix}cliq_sl_brands WHERE slug = %s AND source_id IS NULL", $slug)) > 0) {
            $slug = $base . '-' . $i++;
        }

        return $slug;
    }
}
