<?php

namespace App\Http\Controllers;

use App\Models\GtsOrder;
use App\Models\GtsProvider;
use App\Models\GtsService;
use App\Models\GtsWallet;
use App\Models\GtsWalletTransaction;
use App\Models\PaymentMethod;
use App\Models\User;
use App\Services\GtsProviderApi;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Schema;
use Illuminate\View\View;

class GtsAdminController extends Controller
{
    public function __construct(private readonly GtsProviderApi $providerApi)
    {
    }

    public function dashboard(): View
    {
        if (! Schema::hasTable('gts_orders') || ! Schema::hasTable('gts_services') || ! Schema::hasTable('gts_wallet_transactions')) {
            return view('gts-admin.dashboard', [
                'metrics' => [
                    'GTS Clients' => User::where('role', 'gts_client')->count(),
                    'Pending Deposits' => 0,
                    'Paid Orders' => 0,
                    'Delivery Queue' => 0,
                ],
                'mix' => [],
                'tracking' => collect(),
                'deliveryTasks' => collect(),
            ]);
        }

        $paidOrders = GtsOrder::whereIn('status', ['processing', 'partial', 'completed'])->count();
        $pendingDeposits = GtsWalletTransaction::where('type', 'credit')->where('status', 'pending')->count();
        $activeOrders = GtsOrder::whereIn('status', ['queued', 'processing', 'partial'])->count();
        $serviceMixTotal = max(GtsOrder::count(), 1);
        $serviceMix = GtsOrder::query()
            ->join('gts_services', 'gts_services.id', '=', 'gts_orders.gts_service_id')
            ->selectRaw('gts_services.category, count(*) as total')
            ->groupBy('gts_services.category')
            ->orderByDesc('total')
            ->take(4)
            ->get()
            ->map(fn ($row) => [$row->category, (int) round(($row->total / $serviceMixTotal) * 100), $row->total.' orders'])
            ->all();

        return view('gts-admin.dashboard', [
            'metrics' => [
                'GTS Clients' => User::where('role', 'gts_client')->count(),
                'Pending Deposits' => $pendingDeposits,
                'Paid Orders' => $paidOrders,
                'Delivery Queue' => $activeOrders,
            ],
            'mix' => $serviceMix,
            'tracking' => GtsWalletTransaction::with(['wallet.user', 'paymentMethod'])->where('type', 'credit')->latest()->take(5)->get(),
            'deliveryTasks' => GtsOrder::with(['user', 'service'])->latest()->take(5)->get(),
        ]);
    }

    public function apiManager(Request $request): View
    {
        if (! Schema::hasTable('gts_providers')) {
            abort(503, 'GTS provider tables are not migrated yet. Run php artisan migrate first.');
        }

        $servicesReady = Schema::hasTable('gts_services');
        $providers = $servicesReady
            ? GtsProvider::withCount([
                'services',
                'services as active_services_count' => fn ($query) => $query->where('status', 'active'),
            ])->latest()->get()
            : GtsProvider::latest()->get()->each(function (GtsProvider $provider): void {
                $provider->setAttribute('services_count', 0);
                $provider->setAttribute('active_services_count', 0);
            });

        $providerIds = $providers->pluck('id');
        $ordersReady = Schema::hasTable('gts_orders') && $servicesReady;
        $servicesQuery = $servicesReady ? GtsService::with('provider')->whereIn('gts_provider_id', $providerIds) : null;
        $ordersQuery = $ordersReady ? GtsOrder::with(['user', 'service.provider'])->whereHas('service', fn ($query) => $query->whereIn('gts_provider_id', $providerIds)) : null;

        $selectedProvider = $providers->firstWhere('id', $request->integer('provider_id'));
        $liveServices = $selectedProvider ? $this->providerApi->fetchServices($selectedProvider) : ['services' => [], 'error' => null];
        $importedServiceIds = ($selectedProvider && $servicesReady)
            ? GtsService::where('gts_provider_id', $selectedProvider->id)->pluck('provider_service_id')->map(fn ($id) => (string) $id)->all()
            : [];

        return view('gts-admin.api-manager', [
            'providers' => $providers,
            'stats' => [
                'Providers' => $providers->count(),
                'Active Providers' => $providers->where('status', 'active')->count(),
                'Imported Services' => $servicesQuery ? (clone $servicesQuery)->count() : 0,
                'Auto Submit Services' => $servicesQuery ? (clone $servicesQuery)->where('auto_submit', true)->count() : 0,
                'Provider Orders' => $ordersQuery ? (clone $ordersQuery)->count() : 0,
                'API Queue' => $ordersQuery ? (clone $ordersQuery)->whereIn('status', ['queued', 'processing', 'partial'])->count() : 0,
            ],
            'services' => $servicesQuery ? $servicesQuery->latest()->take(12)->get() : collect(),
            'orders' => $ordersQuery ? $ordersQuery->latest()->take(10)->get() : collect(),
            'selectedProvider' => $selectedProvider,
            'liveServices' => $liveServices,
            'importedServiceIds' => $importedServiceIds,
        ]);
    }

    public function index(Request $request): View
    {
        $module = $request->route('module');
        $config = $this->module($module);

        return view('admin.module-index', [
            'routePrefix' => 'gts-admin',
            'module' => $module,
            'config' => $config,
            'items' => $this->queryFor($config, $request)->latest()->paginate(12)->withQueryString(),
            'search' => $request->string('search'),
            'status' => $request->string('status'),
        ]);
    }

    public function create(Request $request): View
    {
        $module = $request->route('module');

        return view('admin.module-form', [
            'routePrefix' => 'gts-admin',
            'module' => $module,
            'config' => $this->module($module),
            'item' => null,
            'lookups' => $this->lookups(),
        ]);
    }

    public function store(Request $request): RedirectResponse
    {
        $module = $request->route('module');
        $config = $this->module($module);
        $data = $this->validated($request, $module);

        if (isset($data['password'])) {
            $data['password'] = Hash::make($data['password']);
        }

        foreach ($config['files'] ?? [] as $field => $folder) {
            if ($request->hasFile($field)) {
                $data[$field] = $request->file($field)->store($folder, 'public');
            }
        }

        $item = $config['model']::create($data);
        $this->afterSaved($module, $item);

        return redirect()->route("gts-admin.{$module}.index")->with('status', $config['singular'].' created.');
    }

    public function edit(Request $request, int $id): View
    {
        $module = $request->route('module');
        $config = $this->module($module);

        return view('admin.module-form', [
            'routePrefix' => 'gts-admin',
            'module' => $module,
            'config' => $config,
            'item' => $config['model']::findOrFail($id),
            'lookups' => $this->lookups(),
        ]);
    }

    public function update(Request $request, int $id): RedirectResponse
    {
        $module = $request->route('module');
        $config = $this->module($module);
        $item = $config['model']::findOrFail($id);
        $data = $this->validated($request, $module, $id);

        if (($data['password'] ?? '') === '') {
            unset($data['password']);
        } elseif (isset($data['password'])) {
            $data['password'] = Hash::make($data['password']);
        }

        foreach ($config['files'] ?? [] as $field => $folder) {
            if ($request->hasFile($field)) {
                $data[$field] = $request->file($field)->store($folder, 'public');
            }
        }

        if ($module === 'gts-wallet-transactions' && $item instanceof GtsWalletTransaction && $item->applied_at) {
            $this->reverseAppliedTransaction($item);
            $data['applied_at'] = null;
        }

        $item->update($data);
        $this->afterSaved($module, $item);

        return redirect()->route("gts-admin.{$module}.index")->with('status', $config['singular'].' updated.');
    }

    public function destroy(Request $request, int $id): RedirectResponse
    {
        $module = $request->route('module');
        $config = $this->module($module);
        $item = $config['model']::findOrFail($id);

        if ($module === 'gts-wallet-transactions' && $item instanceof GtsWalletTransaction) {
            $this->reverseAppliedTransaction($item);
        }

        $item->delete();

        return back()->with('status', $config['singular'].' deleted.');
    }

    public function syncProvider(Request $request, int $provider): RedirectResponse
    {
        $this->ensureTables(['gts_providers', 'gts_services']);
        $provider = GtsProvider::findOrFail($provider);
        $data = $request->validate(['profit_percent' => ['required', 'numeric', 'min:0', 'max:1000']]);
        $this->providerApi->syncServices($provider, (float) $data['profit_percent']);
        $this->providerApi->syncBalance($provider);

        return back()->with('status', 'Provider synced with '.$data['profit_percent'].'% commission.');
    }

    public function importProviderServices(Request $request, int $provider): RedirectResponse
    {
        $this->ensureTables(['gts_providers', 'gts_services']);
        $provider = GtsProvider::findOrFail($provider);
        $data = $request->validate([
            'service_ids' => ['required', 'array', 'min:1'],
            'service_ids.*' => ['required', 'string', 'max:100'],
            'bulk_profit_percent' => ['required', 'numeric', 'min:0', 'max:1000'],
            'profit_percent_by_service' => ['nullable', 'array'],
            'profit_percent_by_service.*' => ['nullable', 'numeric', 'min:0', 'max:1000'],
        ]);

        $imported = $this->providerApi->importSelectedServices(
            $provider,
            $data['service_ids'],
            (float) $data['bulk_profit_percent'],
            $data['profit_percent_by_service'] ?? []
        );
        $this->providerApi->syncBalance($provider);

        return redirect()
            ->route('gts-admin.api-manager', ['provider_id' => $provider->id])
            ->with('status', $imported.' selected provider services imported with commission.');
    }

    public function syncOrder(int $order): RedirectResponse
    {
        $this->ensureTables(['gts_orders']);
        $order = GtsOrder::findOrFail($order);
        $this->providerApi->syncOrder($order);

        return back()->with('status', 'GTS order synced with provider.');
    }

    private function module(string $module): array
    {
        $tables = [
            'gts-providers' => 'gts_providers',
            'gts-services' => 'gts_services',
            'gts-orders' => 'gts_orders',
            'gts-wallets' => 'gts_wallets',
            'gts-wallet-transactions' => 'gts_wallet_transactions',
        ];

        if (isset($tables[$module])) {
            $this->ensureTables([$tables[$module]]);
        }

        return match ($module) {
            'gts-clients' => [
                'title' => 'GTS Client Management',
                'singular' => 'GTS Client',
                'model' => User::class,
                'where' => ['role' => 'gts_client'],
                'fields' => ['name', 'business_name', 'email', 'phone', 'notes', 'status', 'password'],
                'columns' => ['name', 'business_name', 'email', 'phone', 'status'],
            ],
            'gts-providers' => [
                'title' => 'GTS API Providers',
                'singular' => 'GTS Provider',
                'model' => GtsProvider::class,
                'fields' => ['name', 'api_url', 'api_key', 'status', 'balance'],
                'columns' => ['name', 'api_url', 'status', 'balance', 'last_synced_at'],
            ],
            'gts-services' => [
                'title' => 'GTS Services',
                'singular' => 'GTS Service',
                'model' => GtsService::class,
                'fields' => ['gts_provider_id', 'provider_service_id', 'category', 'name', 'description', 'min_quantity', 'max_quantity', 'provider_rate_per_1000', 'profit_percent', 'rate_per_1000', 'auto_submit', 'refill_enabled', 'dripfeed_enabled', 'status'],
                'columns' => ['category', 'name', 'provider_service_id', 'min_quantity', 'max_quantity', 'rate_per_1000', 'auto_submit', 'status'],
            ],
            'gts-orders' => [
                'title' => 'GTS Orders',
                'singular' => 'GTS Order',
                'model' => GtsOrder::class,
                'fields' => ['user_id', 'gts_service_id', 'order_number', 'provider_order_id', 'target_link', 'quantity', 'charge', 'progress', 'notes', 'status', 'provider_status', 'start_count', 'remains'],
                'columns' => ['order_number', 'provider_order_id', 'user_id', 'gts_service_id', 'quantity', 'charge', 'progress', 'provider_status', 'status'],
            ],
            'gts-wallets' => [
                'title' => 'GTS Wallets',
                'singular' => 'GTS Wallet',
                'model' => GtsWallet::class,
                'fields' => ['user_id', 'balance', 'currency', 'status'],
                'columns' => ['user_id', 'balance', 'currency', 'status'],
            ],
            'gts-wallet-transactions' => [
                'title' => 'GTS Wallet Transactions',
                'singular' => 'Wallet Transaction',
                'model' => GtsWalletTransaction::class,
                'fields' => ['gts_wallet_id', 'payment_method_id', 'type', 'amount', 'reference', 'sender_account', 'proof_path', 'note', 'status'],
                'files' => ['proof_path' => 'gts-deposits'],
                'columns' => ['gts_wallet_id', 'payment_method_id', 'type', 'amount', 'reference', 'status'],
            ],
            default => abort(404),
        };
    }

    private function validated(Request $request, string $module, ?int $id = null): array
    {
        $rules = match ($module) {
            'gts-clients' => ['name' => 'required|string|max:255', 'business_name' => 'nullable|string|max:255', 'email' => 'required|email|max:255|unique:users,email,'.$id, 'phone' => 'nullable|string|max:50', 'notes' => 'nullable|string', 'status' => 'required|string|max:50', 'password' => ($id ? 'nullable' : 'required').'|string|min:8'],
            'gts-providers' => ['name' => 'required|string|max:255', 'api_url' => 'required|url|max:500', 'api_key' => ($id ? 'nullable' : 'required').'|string|max:1000', 'status' => 'required|string|max:50', 'balance' => 'required|numeric|min:0'],
            'gts-services' => ['gts_provider_id' => 'nullable|exists:gts_providers,id', 'provider_service_id' => 'nullable|string|max:100', 'category' => 'required|string|max:100', 'name' => 'required|string|max:255', 'description' => 'nullable|string', 'min_quantity' => 'required|integer|min:1', 'max_quantity' => 'required|integer|min:1', 'provider_rate_per_1000' => 'required|numeric|min:0', 'profit_percent' => 'required|numeric|min:0', 'rate_per_1000' => 'required|numeric|min:0', 'auto_submit' => 'required|boolean', 'refill_enabled' => 'required|boolean', 'dripfeed_enabled' => 'required|boolean', 'status' => 'required|string|max:50'],
            'gts-orders' => ['user_id' => 'required|exists:users,id', 'gts_service_id' => 'required|exists:gts_services,id', 'order_number' => 'required|string|max:80|unique:gts_orders,order_number,'.$id, 'provider_order_id' => 'nullable|string|max:100', 'target_link' => 'required|string|max:500', 'quantity' => 'required|integer|min:1', 'charge' => 'required|numeric|min:0', 'progress' => 'required|integer|min:0|max:100', 'notes' => 'nullable|string', 'status' => 'required|string|max:50', 'provider_status' => 'nullable|string|max:100', 'start_count' => 'nullable|integer|min:0', 'remains' => 'nullable|integer|min:0'],
            'gts-wallets' => ['user_id' => 'required|exists:users,id|unique:gts_wallets,user_id,'.$id, 'balance' => 'required|numeric|min:0', 'currency' => 'required|string|max:20', 'status' => 'required|string|max:50'],
            'gts-wallet-transactions' => ['gts_wallet_id' => 'required|exists:gts_wallets,id', 'payment_method_id' => 'nullable|exists:payment_methods,id', 'type' => 'required|string|max:30', 'amount' => 'required|numeric|min:0', 'reference' => 'nullable|string|max:255', 'sender_account' => 'nullable|string|max:255', 'proof_path' => 'nullable|file|mimes:'.\App\Models\SystemSetting::uploadExtensions().',jpg,jpeg,png,webp,pdf|max:'.\App\Models\SystemSetting::uploadLimitKilobytes(), 'note' => 'nullable|string', 'status' => 'required|string|max:50'],
            default => abort(404),
        };

        $data = $request->validate($rules);

        if ($module === 'gts-clients') {
            $data['role'] = 'gts_client';
        }

        if ($module === 'gts-providers' && $id && empty($data['api_key'])) {
            unset($data['api_key']);
        }

        unset($data['proof_path']);

        return $data;
    }

    private function lookups(): array
    {
        return [
            'gtsClients' => User::where('role', 'gts_client')->orderBy('name')->get(),
            'gtsProviders' => Schema::hasTable('gts_providers') ? GtsProvider::orderBy('name')->get() : collect(),
            'gtsServices' => Schema::hasTable('gts_services') ? GtsService::orderBy('category')->orderBy('name')->get() : collect(),
            'gtsWallets' => Schema::hasTable('gts_wallets') ? GtsWallet::with('user')->orderByDesc('created_at')->get() : collect(),
            'paymentMethods' => PaymentMethod::orderBy('name')->get(),
        ];
    }

    private function queryFor(array $config, Request $request)
    {
        $query = $config['model']::query();

        foreach (($config['where'] ?? []) as $field => $value) {
            $query->where($field, $value);
        }

        if ($request->filled('search')) {
            $search = $request->string('search')->toString();
            $query->where(function ($filter) use ($config, $search): void {
                foreach ($config['columns'] as $column) {
                    if (! str_ends_with($column, '_id')) {
                        $filter->orWhere($column, 'like', "%{$search}%");
                    }
                }
            });
        }

        if ($request->filled('status')) {
            $query->where('status', $request->string('status')->toString());
        }

        return $query;
    }

    private function afterSaved(string $module, Model $item): void
    {
        if ($module === 'gts-clients' && Schema::hasTable('gts_wallets')) {
            GtsWallet::firstOrCreate(['user_id' => $item->id], ['currency' => 'PKR', 'status' => 'active']);
        }

        if ($module === 'gts-wallet-transactions' && $item instanceof GtsWalletTransaction) {
            $this->applyTransaction($item);
        }

        if ($module === 'gts-orders' && $item instanceof GtsOrder) {
            $progress = match ($item->status) {
                'queued', 'pending' => max((int) $item->progress, 10),
                'processing', 'in_progress' => max((int) $item->progress, 45),
                'partial' => max((int) $item->progress, 75),
                'completed', 'approved', 'delivered' => 100,
                'rejected' => min((int) $item->progress, 20),
                default => (int) $item->progress,
            };
            $item->forceFill(['progress' => $progress])->save();
        }
    }

    private function applyTransaction(GtsWalletTransaction $transaction): void
    {
        if ($transaction->status !== 'completed' || $transaction->applied_at) {
            return;
        }

        DB::transaction(function () use ($transaction): void {
            $wallet = GtsWallet::whereKey($transaction->gts_wallet_id)->lockForUpdate()->first();
            if (! $wallet) {
                return;
            }

            $amount = (float) $transaction->amount;
            $wallet->balance = $transaction->type === 'debit'
                ? max(0, (float) $wallet->balance - $amount)
                : (float) $wallet->balance + $amount;
            $wallet->save();
            $transaction->forceFill(['applied_at' => now()])->save();
        });
    }

    private function reverseAppliedTransaction(GtsWalletTransaction $transaction): void
    {
        if ($transaction->status !== 'completed' || ! $transaction->applied_at) {
            return;
        }

        DB::transaction(function () use ($transaction): void {
            $wallet = GtsWallet::whereKey($transaction->gts_wallet_id)->lockForUpdate()->first();
            if (! $wallet) {
                return;
            }

            $amount = (float) $transaction->amount;
            $wallet->balance = $transaction->type === 'debit'
                ? (float) $wallet->balance + $amount
                : max(0, (float) $wallet->balance - $amount);
            $wallet->save();
        });
    }

    private function ensureTables(array $tables): void
    {
        foreach ($tables as $table) {
            if (! Schema::hasTable($table)) {
                abort(503, 'GTS tables are not migrated yet. Run php artisan migrate first.');
            }
        }
    }
}
