<?php

namespace App\Repositories;

use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use Mtc\MercuryDataModels\Setting;

class SiteExportRepository
{
    /**
     * Tables that are always included in export
     */
    protected const CORE_TABLES = [
        'settings',
        'pages',
        'page_content',
        'page_versions',
        'page_templates',
        'page_template_elements',
        'menus',
        'menu_entries',
        'global_content',
        'content_elements',
        'content_element_fields',
        'forms',
        'form_questions',
        'form_sections',
        'dealerships',
        'dealership_content',
        'dealership_franchises',
        'franchises',
        'labels',
        'media',
        'media_uses',
        'media_sizes',
        'seo_data',
        'seo_defaults',
        'seo_redirects',
        'shortcodes',
        'filter_features',
        'enquiry_statuses',
        'enquiry_types',
        'reservation_prices',
        'reservation_statuses',
        'replacements',
        'colour_mapping',
        'property_categories',
        'properties',
    ];

    /**
     * Vehicle-related tables (optional)
     */
    protected const VEHICLE_TABLES = [
        'vehicles',
        'vehicle_finance',
        'vehicle_tech_data',
        'vehicle_standard_equipment',
        'vehicle_offers',
        'vehicle_offer_content',
        'vehicle_offer_content_history',
        'vehicle_offer_finance',
        'vehicle_offer_fuel_types',
        'vehicle_features',
        'vehicle_attributes',
        'vehicle_attribute_values',
        'vehicle_autotrader_data',
        'vehicle_price_history',
        'vehicle_labels',
        'vehicle_offer_labels',
        'vehicle_views',
        'offer_views',
        'lease_vehicle_labels',
        'vehicle_trims',
        'new_cars',
        'new_car_content',
        'new_car_content_history',
        'new_car_finance',
        'catalog_offers',
        'catalog_offer_rules',
        'car_configurator_models',
        'car_configurator_packages',
        'car_configurator_extras',
    ];

    /**
     * Tables that are always excluded
     */
    protected const EXCLUDED_TABLES = [
        'api_notifications',
        'audits',
        'vehicle_stock_sync_logs',
        'migrations',
        'personal_access_tokens',
        'telescope_entries',
        'telescope_entries_tags',
        'telescope_monitoring',
        'jobs',
        'failed_jobs',
        'cache',
        'cache_locks',
        'sessions',
        'password_resets',
        'password_reset_tokens',
    ];

    /**
     * Get list of tables that can be exported
     */
    public function getExportableTables(bool $includeVehicles): array
    {
        $tables = self::CORE_TABLES;

        if ($includeVehicles) {
            $tables = array_merge($tables, self::VEHICLE_TABLES);
        }

        return array_diff($tables, self::EXCLUDED_TABLES);
    }

    /**
     * Get table list with record counts
     */
    public function getTablesWithCounts(bool $includeVehicles): array
    {
        $tables = $this->getExportableTables($includeVehicles);

        $tableData = [];
        foreach ($tables as $table) {
            if (Schema::hasTable($table)) {
                $tableData[] = [
                    'name' => $table,
                    'count' => DB::table($table)->count(),
                ];
            }
        }

        return $tableData;
    }

    /**
     * Check if a table is allowed for export
     */
    public function isTableAllowed(string $table, bool $includeVehicles): bool
    {
        return in_array($table, $this->getExportableTables($includeVehicles));
    }

    /**
     * Get paginated data for a table
     */
    public function getTableData(string $table, int $page = 1, int $perPage = 100): array
    {
        $data = DB::table($table)
            ->orderBy('id')
            ->paginate($perPage, ['*'], 'page', $page);

        return [
            'data' => $data->items(),
            'pagination' => [
                'current_page' => $data->currentPage(),
                'last_page' => $data->lastPage(),
                'per_page' => $data->perPage(),
                'total' => $data->total(),
            ],
        ];
    }

    /**
     * Get settings with special handling for encrypted secrets
     */
    public function getSettingsData(string $encryptionKey, int $page = 1, int $perPage = 100): array
    {
        $settings = Setting::query()
            ->orderBy('id')
            ->paginate($perPage, ['*'], 'page', $page);

        $data = $settings->map(function (Setting $setting) use ($encryptionKey) {
            $row = $setting->toArray();

            // Re-encrypt secret values using the auth token
            if ($setting->type === 'secret' && $setting->value !== null) {
                $row['value'] = $this->encryptWithToken($setting->value, $encryptionKey);
                $row['encrypted_with_token'] = true;
            }

            return $row;
        });

        return [
            'data' => $data->values()->toArray(),
            'pagination' => [
                'current_page' => $settings->currentPage(),
                'last_page' => $settings->lastPage(),
                'per_page' => $settings->perPage(),
                'total' => $settings->total(),
            ],
        ];
    }

    /**
     * Encrypt a value using the provided token as key
     */
    public function encryptWithToken(string $value, string $token): string
    {
        $key = hash('sha256', $token, true);
        $iv = openssl_random_pseudo_bytes(16);
        $encrypted = openssl_encrypt($value, 'AES-256-CBC', $key, 0, $iv);

        return base64_encode($iv . '::' . $encrypted);
    }

    /**
     * Decrypt a value that was encrypted with the auth token
     */
    public function decryptWithToken(string $encryptedValue, string $token): string
    {
        $key = hash('sha256', $token, true);
        $parts = explode('::', base64_decode($encryptedValue), 2);

        if (count($parts) !== 2) {
            throw new \RuntimeException('Invalid encrypted value format');
        }

        [$iv, $encrypted] = $parts;

        $decrypted = openssl_decrypt($encrypted, 'AES-256-CBC', $key, 0, $iv);

        if ($decrypted === false) {
            throw new \RuntimeException('Failed to decrypt value');
        }

        return $decrypted;
    }
}
