<?php

namespace App\Http\Controllers;

use App\Models\BackupExport;
use App\Models\BrandSetting;
use App\Models\NotificationSetting;
use App\Models\PaymentMethod;
use App\Models\RolePermission;
use App\Models\SecurityLog;
use App\Models\Setting;
use App\Models\SystemSetting;
use App\Models\Task;
use App\Models\User;
use App\Models\WebsiteSetting;
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\Support\Facades\Storage;
use Illuminate\Validation\Rule;
use Illuminate\View\View;
use Symfony\Component\HttpFoundation\StreamedResponse;

class SettingsController extends Controller
{
    public const SECTIONS = ['profile', 'website', 'brand', 'payment', 'roles', 'notifications', 'security', 'system', 'modules', 'backup'];

    public const MODULE_CONTROLS = [
        'Auth & Public Access' => [
            'demo_login_enabled' => ['Demo login buttons', 'Show Admin, Client, Designer, and GTS demo buttons on the login page.', true],
            'client_registration_enabled' => ['Client registration access', 'Allow new agency clients to create a plan-client portal account from the public register page.', false],
            'gts_landing_enabled' => ['GTS public landing page', 'Show the Ghaffar Trusted Seller section and public /gts page.', true],
        ],
        'Portal Access' => [
            'client_portal_enabled' => ['Client portal access', 'Allow regular agency clients to open their dashboard and portal pages.', true],
            'gts_portal_enabled' => ['GTS client portal access', 'Allow GTS clients to open wallet, services, orders, and settings pages.', true],
        ],
        'GTS Selling Flow' => [
            'gts_api_manager_enabled' => ['GTS API manager access', 'Allow admins to open provider API manager, service sync, and provider order monitoring.', true],
            'gts_order_create_enabled' => ['GTS order create', 'Allow GTS clients to create new SMM service orders from wallet balance.', true],
            'gts_deposit_enabled' => ['GTS deposit access', 'Allow GTS clients to submit wallet deposit requests with proof.', true],
        ],
    ];

    public const PERMISSIONS = [
        'view_dashboard' => 'View Dashboard',
        'manage_clients' => 'Manage Clients',
        'manage_designers' => 'Manage Designers',
        'manage_plans' => 'Manage Plans',
        'manage_tasks' => 'Manage Tasks',
        'manage_payments' => 'Manage Payments',
        'manage_portfolio' => 'Manage Portfolio',
        'view_reports' => 'View Reports',
        'edit_settings' => 'Edit Settings',
        'upload_files' => 'Upload Files',
        'approve_work' => 'Approve Work',
        'submit_requests' => 'Submit Requests',
        'download_files' => 'Download Files',
    ];

    public const PERMISSION_GROUPS = [
        'Workspace' => ['view_dashboard', 'view_reports', 'edit_settings'],
        'Agency Management' => ['manage_clients', 'manage_designers', 'manage_plans', 'manage_payments', 'manage_portfolio'],
        'Delivery' => ['manage_tasks', 'approve_work', 'upload_files', 'download_files'],
        'Client Requests' => ['submit_requests'],
    ];

    public const NOTIFICATIONS = [
        'new_client_registered' => 'New Client Registered',
        'new_request_submitted' => 'New Request Submitted',
        'new_payment_proof_uploaded' => 'New Payment Proof Uploaded',
        'task_assigned' => 'Task Assigned',
        'task_deadline_reminder' => 'Task Deadline Reminder',
        'revision_required' => 'Revision Required',
        'work_completed' => 'Work Completed',
        'payment_approved' => 'Payment Approved',
        'payment_rejected' => 'Payment Rejected',
    ];

    public function index(): RedirectResponse
    {
        return redirect()->route('admin.settings.section', 'profile');
    }

    public function show(Request $request, string $section): View
    {
        abort_unless(in_array($section, self::SECTIONS, true), 404);
        $this->seedNotificationSettings();

        return view('admin.settings.module', [
            'active' => $section,
            'admin' => $request->user(),
            'website' => WebsiteSetting::current(),
            'brand' => BrandSetting::current(),
            'methods' => PaymentMethod::orderBy('name')->get(),
            'roles' => ['super_admin' => 'Super Admin', 'admin' => 'Admin', 'designer' => 'Designer', 'client' => 'Client', 'gts_client' => 'GTS Client'],
            'permissionLabels' => self::PERMISSIONS,
            'permissionGroups' => self::PERMISSION_GROUPS,
            'permissionMatrix' => RolePermission::all()->groupBy('role')->map->keyBy('permission_name'),
            'notificationLabels' => self::NOTIFICATIONS,
            'notificationSettings' => NotificationSetting::all()->keyBy('notification_type'),
            'securityLogs' => SecurityLog::with('user')->latest('login_time')->take(5)->get(),
            'activeSessions' => Schema::hasTable('sessions') && Schema::hasColumn('sessions', 'user_id')
                ? DB::table('sessions')->where('user_id', $request->user()->id)->latest('last_activity')->take(5)->get()
                : collect(),
            'failedAttempts' => SecurityLog::where('status', 'failed')->latest('login_time')->take(5)->get(),
            'system' => SystemSetting::current(),
            'moduleControls' => self::MODULE_CONTROLS,
            'exports' => BackupExport::latest()->take(8)->get(),
        ]);
    }

    public function updateProfile(Request $request): RedirectResponse
    {
        $user = $request->user();
        $data = $request->validate([
            'name' => ['required', 'string', 'max:255'],
            'email' => ['required', 'email', 'max:255', Rule::unique('users')->ignore($user->id)],
            'phone' => ['nullable', 'string', 'max:50'],
            'bio' => ['nullable', 'string', 'max:1000'],
            'profile_image' => ['nullable', 'image', 'max:4096'],
        ]);

        if ($request->hasFile('profile_image')) {
            $data['profile_image'] = $request->file('profile_image')->store('profiles', 'public');
            $data['avatar_path'] = $data['profile_image'];
        }

        $user->update($data);

        return back()->with('status', 'Profile settings saved.');
    }

    public function updateWebsite(Request $request): RedirectResponse
    {
        $website = WebsiteSetting::current();
        $data = $request->validate([
            'website_name' => ['required', 'string', 'max:255'],
            'agency_name' => ['required', 'string', 'max:255'],
            'tagline' => ['nullable', 'string', 'max:255'],
            'logo' => ['nullable', 'image', 'max:4096'],
            'favicon' => ['nullable', 'image', 'max:1024'],
            'contact_number' => ['nullable', 'string', 'max:80'],
            'whatsapp_number' => ['nullable', 'string', 'max:80'],
            'email' => ['nullable', 'email', 'max:255'],
            'address' => ['nullable', 'string', 'max:1000'],
            'facebook' => ['nullable', 'url', 'max:255'],
            'instagram' => ['nullable', 'url', 'max:255'],
            'tiktok' => ['nullable', 'url', 'max:255'],
            'youtube' => ['nullable', 'url', 'max:255'],
            'footer_text' => ['nullable', 'string', 'max:1000'],
            'gts_landing_enabled' => ['required', 'boolean'],
        ]);

        $gtsLandingEnabled = $data['gts_landing_enabled'];
        unset($data['gts_landing_enabled']);

        foreach (['logo', 'favicon'] as $file) {
            if ($request->hasFile($file)) {
                $data[$file] = $request->file($file)->store('website', 'public');
            }
        }

        $website->update($data);
        Setting::putMany([
            'brand_name' => $website->agency_name,
            'phone' => $website->contact_number,
            'email' => $website->email,
            'address' => $website->address,
            'gts_landing_enabled' => $gtsLandingEnabled ? '1' : '0',
        ]);

        return back()->with('status', 'Website settings saved.');
    }

    public function updateBrand(Request $request): RedirectResponse
    {
        BrandSetting::current()->update($request->validate([
            'primary_color' => ['required', 'regex:/^#[0-9A-Fa-f]{6}$/'],
            'secondary_color' => ['required', 'regex:/^#[0-9A-Fa-f]{6}$/'],
            'background_color' => ['required', 'regex:/^#[0-9A-Fa-f]{6}$/'],
            'text_color' => ['required', 'regex:/^#[0-9A-Fa-f]{6}$/'],
            'font_family' => ['required', 'in:Inter,Poppins,System'],
            'button_radius' => ['required', 'integer', 'min:8', 'max:32'],
            'card_radius' => ['required', 'integer', 'min:12', 'max:36'],
            'gradient_enabled' => ['required', 'boolean'],
            'animations_enabled' => ['required', 'boolean'],
        ]));

        return back()->with('status', 'Brand theme saved.');
    }

    public function savePaymentMethod(Request $request): RedirectResponse
    {
        $data = $request->validate([
            'id' => ['nullable', 'exists:payment_methods,id'],
            'name' => ['required', 'string', 'max:255'],
            'account_title' => ['nullable', 'string', 'max:255'],
            'account_number' => ['nullable', 'string', 'max:255'],
            'bank_name' => ['nullable', 'string', 'max:255'],
            'instructions' => ['nullable', 'string', 'max:1000'],
            'status' => ['required', 'in:active,inactive'],
            'qr_code' => ['nullable', 'image', 'max:4096'],
        ]);

        $method = isset($data['id']) ? PaymentMethod::findOrFail($data['id']) : new PaymentMethod();
        unset($data['id']);
        $data['method_name'] = $data['name'];
        if ($request->hasFile('qr_code')) {
            $data['qr_code'] = $request->file('qr_code')->store('payment-qr', 'public');
        }
        $method->fill($data)->save();

        return back()->with('status', 'Payment method saved.');
    }

    public function deletePaymentMethod(PaymentMethod $method): RedirectResponse
    {
        $method->delete();

        return back()->with('status', 'Payment method deleted.');
    }

    public function updateRoles(Request $request): RedirectResponse
    {
        $allowed = $request->input('permissions', []);
        foreach (array_keys($request->validate(['roles' => ['required', 'array']])['roles']) as $role) {
            foreach (array_keys(self::PERMISSIONS) as $permission) {
                RolePermission::updateOrCreate(['role' => $role, 'permission_name' => $permission], [
                    'allowed' => $role === 'super_admin' ? true : (bool) data_get($allowed, "{$role}.{$permission}"),
                ]);
            }
        }

        return back()->with('status', 'Role permissions saved.');
    }

    public function updateNotifications(Request $request): RedirectResponse
    {
        foreach (array_keys(self::NOTIFICATIONS) as $type) {
            NotificationSetting::updateOrCreate(['notification_type' => $type], [
                'dashboard_enabled' => $request->boolean("settings.{$type}.dashboard"),
                'email_enabled' => $request->boolean("settings.{$type}.email"),
                'whatsapp_enabled' => $request->boolean("settings.{$type}.whatsapp"),
                'reminder_time' => $request->input("settings.{$type}.reminder_time", 'same_day'),
            ]);
        }

        return back()->with('status', 'Notification settings saved.');
    }

    public function updateSecurity(Request $request): RedirectResponse
    {
        $data = $request->validate([
            'current_password' => ['nullable', 'required_with:password', 'current_password'],
            'password' => ['nullable', 'string', 'min:8', 'confirmed'],
            'two_factor_enabled' => ['nullable', 'boolean'],
        ]);

        if (! empty($data['password'])) {
            $request->user()->update(['password' => Hash::make($data['password'])]);
        }

        Setting::putMany(['two_factor_admin_'.$request->user()->id => $request->boolean('two_factor_enabled') ? '1' : '0']);

        return back()->with('status', 'Security settings saved.');
    }

    public function logoutSessions(Request $request): RedirectResponse
    {
        if (Schema::hasTable('sessions') && Schema::hasColumn('sessions', 'user_id')) {
            DB::table('sessions')->where('user_id', $request->user()->id)->where('id', '!=', $request->session()->getId())->delete();
        }

        return back()->with('status', 'Other active sessions were logged out.');
    }

    public function updateSystem(Request $request): RedirectResponse
    {
        $data = $request->validate([
            'currency' => ['required', 'string', 'max:20'],
            'date_format' => ['required', 'string', 'max:40'],
            'timezone' => ['required', 'timezone', 'max:120'],
            'default_task_deadline' => ['required', 'integer', 'min:1', 'max:90'],
            'auto_assign_designer' => ['required', 'boolean'],
            'auto_approve_payment' => ['required', 'boolean'],
            'file_upload_limit' => ['required', 'integer', 'min:1', 'max:100'],
            'allowed_file_types' => ['required', 'string', 'max:255', 'regex:/^[A-Za-z0-9,\\s]+$/'],
            'maintenance_mode' => ['required', 'boolean'],
        ]);

        $data['allowed_file_types'] = collect(explode(',', $data['allowed_file_types']))
            ->map(fn (string $extension) => strtolower(trim($extension)))
            ->filter(fn (string $extension) => $extension !== '')
            ->unique()
            ->implode(',');

        SystemSetting::current()->update($data);

        return back()->with('status', 'System settings saved.');
    }

    public function backupOptions(Request $request): RedirectResponse
    {
        Setting::putMany($request->validate(['auto_backup' => ['required', 'boolean'], 'backup_frequency' => ['required', 'in:daily,weekly,monthly']]));

        return back()->with('status', 'Backup schedule saved.');
    }

    public function updateModules(Request $request): RedirectResponse
    {
        $keys = collect(self::MODULE_CONTROLS)->flatMap(fn ($items) => array_keys($items))->values();
        $settings = $keys->mapWithKeys(fn ($key) => [$key => $request->boolean($key) ? '1' : '0'])->all();

        Setting::putMany($settings);

        return back()->with('status', 'Module controls saved.');
    }

    public function export(Request $request, string $type): StreamedResponse
    {
        abort_unless(in_array($type, ['clients', 'payments', 'tasks', 'reports', 'backup'], true), 404);
        $extension = $type === 'reports' ? 'pdf' : ($type === 'backup' ? 'json' : 'csv');
        $fileName = $type.'_'.now()->format('Ymd_His').'.'.$extension;
        $path = 'exports/'.$fileName;
        Storage::disk('public')->put($path, $this->exportContent($type));
        $export = BackupExport::create(['file_name' => $fileName, 'file_type' => $type, 'file_url' => $path, 'created_by' => $request->user()->id]);

        return Storage::disk('public')->download($export->file_url, $export->file_name);
    }

    public function download(BackupExport $export): StreamedResponse
    {
        abort_unless(Storage::disk('public')->exists($export->file_url), 404);

        return Storage::disk('public')->download($export->file_url, $export->file_name);
    }

    private function seedNotificationSettings(): void
    {
        foreach (array_keys(self::NOTIFICATIONS) as $type) {
            NotificationSetting::firstOrCreate(['notification_type' => $type]);
        }
    }

    private function exportContent(string $type): string
    {
        return match ($type) {
            'clients' => $this->csv(['Name', 'Email', 'Phone', 'Status'], User::where('role', 'client')->get()->map(fn ($user) => [$user->name, $user->email, $user->phone, $user->status])->all()),
            'payments' => $this->csv(['Client ID', 'Amount', 'Method', 'Reference', 'Status'], \App\Models\Payment::all()->map(fn ($payment) => [$payment->user_id, $payment->amount, $payment->method, $payment->transaction_reference, $payment->status])->all()),
            'tasks' => $this->csv(['Title', 'Client ID', 'Designer ID', 'Deadline', 'Status'], Task::all()->map(fn ($task) => [$task->title, $task->client_id, $task->designer_id, $task->deadline, $task->status])->all()),
            'reports' => $this->reportPdfContent(),
            default => json_encode([
                'exported_at' => now()->toIso8601String(),
                'users' => User::select(['id', 'name', 'email', 'phone', 'role', 'status'])->get(),
                'tasks' => Task::all(),
                'requests' => \App\Models\ClientRequest::all(),
                'payments' => \App\Models\Payment::all(),
                'plans' => \App\Models\Plan::all(),
                'services' => \App\Models\Service::all(),
                'website_settings' => WebsiteSetting::all(),
                'brand_settings' => BrandSetting::all(),
                'system_settings' => SystemSetting::all(),
                'role_permissions' => RolePermission::all(),
            ], JSON_PRETTY_PRINT),
        };
    }

    private function csv(array $headers, array $rows): string
    {
        return collect([$headers, ...$rows])->map(fn ($row) => collect($row)->map(fn ($cell) => '"'.str_replace('"', '""', (string) $cell).'"')->implode(','))->implode("\n");
    }

    private function reportPdfContent(): string
    {
        $lines = [
            'M Ghaffar Graphics Report',
            'Generated: '.now()->format('M j, Y g:i A'),
            'Clients: '.User::where('role', 'client')->count(),
            'Designers: '.User::where('role', 'designer')->count(),
            'Tasks: '.Task::count(),
            'Payments: '.\App\Models\Payment::count(),
        ];
        $stream = "BT /F1 18 Tf 50 760 Td ";
        foreach ($lines as $index => $line) {
            $escaped = str_replace(['\\', '(', ')'], ['\\\\', '\\(', '\\)'], $line);
            $stream .= ($index === 0 ? '' : '0 -28 Td ')."({$escaped}) Tj ";
        }
        $stream .= 'ET';
        $objects = [
            '1 0 obj << /Type /Catalog /Pages 2 0 R >> endobj',
            '2 0 obj << /Type /Pages /Kids [3 0 R] /Count 1 >> endobj',
            '3 0 obj << /Type /Page /Parent 2 0 R /MediaBox [0 0 612 792] /Resources << /Font << /F1 4 0 R >> >> /Contents 5 0 R >> endobj',
            '4 0 obj << /Type /Font /Subtype /Type1 /BaseFont /Helvetica >> endobj',
            '5 0 obj << /Length '.strlen($stream).' >> stream'."\n{$stream}\n".'endstream endobj',
        ];
        $pdf = "%PDF-1.4\n";
        $offsets = [0];
        foreach ($objects as $object) {
            $offsets[] = strlen($pdf);
            $pdf .= $object."\n";
        }
        $xref = strlen($pdf);
        $pdf .= "xref\n0 ".(count($objects) + 1)."\n0000000000 65535 f \n";
        foreach (array_slice($offsets, 1) as $offset) {
            $pdf .= str_pad((string) $offset, 10, '0', STR_PAD_LEFT)." 00000 n \n";
        }
        $pdf .= "trailer << /Size ".(count($objects) + 1)." /Root 1 0 R >>\nstartxref\n{$xref}\n%%EOF\n";

        return $pdf;
    }
}
