<?php

namespace App\Http\Controllers\Admin;

use App\DataTables\MikrotikDataTable;
use App\DataTables\MikrotikDisconnectDueDataTable;
use App\DataTables\MikrotikOnlineDataTable;
use App\DataTables\MikrotikOnlineStaticDataTable;
use App\DataTables\MikrotikPppOfflineDataTable;
use App\DataTables\MikrotikPppUnmatchingDataTable;
use App\DataTables\MikrotikStaticUnmatchingDataTable;
use App\Http\Controllers\Controller;
use App\Http\Requests\StoreMikrotikRequest;
use App\Http\Requests\UpdateMikrotikRequest;
use App\Jobs\UploadImportedCustomersToMikrotikJob;
use App\Jobs\TestMikrotikConnectionJob;
use App\Models\ActivityLog;
use App\Models\Customer;
use App\Models\Mikrotik;
use App\Rules\HostOrIpAddress;
use App\Services\MikrotikService;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;

class MikrotikController extends Controller
{
    public function __construct(protected MikrotikService $service)
    {
        $this->authorizeResource(Mikrotik::class, 'mikrotik');
        $this->middleware('can:mikrotik.view')->only([
            'index',
            'data',
            'profiles',
            'disconnectByDue',
            'disconnectByDueData',
            'online',
            'onlineData',
            'pppoeOffline',
            'pppoeOfflineData',
            'onlineStatic',
            'onlineStaticData',
            'pppoeUnmatching',
            'pppoeSoftwareUnmatching',
            'pppoeMikrotikUnmatching',
            'pppoeUnmatchingData',
            'staticUnmatching',
            'staticUnmatchingData',
        ]);
        $this->middleware('can:mikrotik.create')->only(['store']);
        $this->middleware('can:mikrotik.update')->only(['update', 'edit', 'toggleStatus', 'importSecrets', 'uploadCustomers', 'uploadOneCustomer', 'importOneSecret']);
        $this->middleware('can:mikrotik.delete')->only(['destroy']);
    }

    public function index()
    {
        return view('admin.mikrotiks.index');
    }

    public function data(Request $request, MikrotikDataTable $dataTable)
    {
        return $dataTable->render($request);
    }

    public function testConnection(Request $request)
    {
        abort_unless($request->user()->can('mikrotik.create') || $request->user()->can('mikrotik.update'), 403);

        $payload = $request->validate([
            'name' => ['required', 'string', 'max:255'],
            'ip_address' => ['required', new HostOrIpAddress],
            'api_port' => ['required', 'integer', 'min:1', 'max:65535'],
            'username' => ['required', 'string', 'max:255'],
            'password' => ['nullable', 'string', 'max:255'],
            'ssl_enabled' => ['sometimes', 'boolean'],
            'mikrotik_id' => ['nullable', 'integer'],
        ]);

        if (empty($payload['password']) && ! empty($payload['mikrotik_id'])) {
            $existing = Mikrotik::find($payload['mikrotik_id']);
            if (! $existing) {
                return response()->json([
                    'success' => false,
                    'message' => 'This MikroTik server no longer exists. Close the form, refresh the list, and try again.',
                ], 422);
            }

            $payload['password'] = $existing->password;
        }

        if (empty($payload['password'])) {
            return response()->json(['success' => false, 'message' => 'Password is required to verify connection.'], 422);
        }

        try {
            $result = $this->service->testConnection($payload);
        } catch (\Throwable $exception) {
            report($exception);

            return response()->json([
                'success' => false,
                'message' => $exception->getMessage() ?: 'MikroTik connection could not be verified.',
            ], 422);
        }

        return response()->json(['success' => true, 'data' => $result]);
    }

    public function profiles()
    {
        $server = Mikrotik::where('status', true)->where('connected', true)
            ->orderByDesc('is_default')
            ->orderByDesc('last_connected_at')
            ->first();

        if (! $server) {
            return response()->json(['profiles' => [], 'message' => 'No connected MikroTik server found.']);
        }

        $serverData = [
            'id' => $server->id,
            'name' => $server->name,
            'ip_address' => $server->ip_address,
            'api_port' => $server->api_port,
        ];

        $cached = Cache::get('mikrotik:profiles:'.$server->id, []);

        if (! empty($cached)) {
            return response()->json(['profiles' => $cached, 'server' => $serverData]);
        }

        return response()->json([
            'profiles' => [],
            'server' => $serverData,
            'message' => 'No cached MikroTik profiles available. Wait for background sync or verify your MikroTik server connection.',
        ]);
    }

    public function disconnectByDue()
    {
        return view('admin.mikrotiks.disconnect-due');
    }

    public function disconnectByDueData(Request $request, MikrotikDisconnectDueDataTable $dataTable)
    {
        return $dataTable->render($request);
    }

    public function online()
    {
        return view('admin.mikrotiks.online');
    }

    public function onlineData(Request $request, MikrotikOnlineDataTable $dataTable)
    {
        return $dataTable->render($request);
    }

    public function pppoeOffline()
    {
        return view('admin.mikrotiks.pppoe-offline');
    }

    public function pppoeOfflineData(Request $request, MikrotikPppOfflineDataTable $dataTable)
    {
        return $dataTable->render($request);
    }

    public function onlineStatic()
    {
        return view('admin.mikrotiks.online-static');
    }

    public function onlineStaticData(Request $request, MikrotikOnlineStaticDataTable $dataTable)
    {
        return $dataTable->render($request);
    }

    public function pppoeUnmatching()
    {
        return redirect()->route('admin.mikrotik.pppoe-software-unmatching');
    }

    public function pppoeSoftwareUnmatching()
    {
        return view('admin.mikrotiks.pppoe-unmatching', ['mode' => 'software']);
    }

    public function pppoeMikrotikUnmatching()
    {
        return view('admin.mikrotiks.pppoe-unmatching', ['mode' => 'mikrotik']);
    }

    public function pppoeUnmatchingData(Request $request, MikrotikPppUnmatchingDataTable $dataTable)
    {
        return $dataTable->render($request);
    }

    public function importSecrets()
    {
        return response()->json(['success' => true, 'data' => $this->service->importRouterSecretsToSoftware()]);
    }

    public function uploadCustomers()
    {
        return response()->json(['success' => true, 'data' => $this->service->uploadSoftwareCustomersToRouters()]);
    }

    public function uploadOneCustomer(Request $request)
    {
        $validated = $request->validate(['customer_id' => ['required', 'integer', 'exists:customers,id']]);
        $customer = Customer::findOrFail($validated['customer_id']);
        abort_unless($customer->mikrotik_id, 422, 'No MikroTik is assigned to this customer.');
        UploadImportedCustomersToMikrotikJob::dispatch($customer->mikrotik_id, [$customer->id]);

        return response()->json(['success' => true, 'message' => 'Customer upload queued successfully.']);
    }

    public function importOneSecret(Request $request)
    {
        $validated = $request->validate([
            'router_id' => ['required', 'integer', 'exists:mikrotiks,id'],
            'identifier' => ['required', 'string', 'max:191'],
        ]);
        $summary = $this->service->importRouterSecretsToSoftware((int) $validated['router_id'], $validated['identifier']);

        return response()->json(['success' => $summary['created'] > 0, 'data' => $summary, 'message' => $summary['created'] > 0 ? 'Customer imported successfully.' : ($summary['messages'][0] ?? 'Customer could not be imported.')], $summary['created'] > 0 ? 200 : 422);
    }

    public function staticUnmatching()
    {
        return view('admin.mikrotiks.static-unmatching');
    }

    public function staticUnmatchingData(Request $request, MikrotikStaticUnmatchingDataTable $dataTable)
    {
        return $dataTable->render($request);
    }

    public function store(StoreMikrotikRequest $request)
    {
        $mikrotik = $this->service->create($request->validated());

        return response()->json(['success' => true, 'data' => $mikrotik], 201);
    }

    public function edit(Mikrotik $mikrotik)
    {
        return response()->json([
            'data' => $mikrotik,
            'update_url' => route('admin.mikrotiks.update', $mikrotik),
        ]);
    }

    public function update(UpdateMikrotikRequest $request, Mikrotik $mikrotik)
    {
        $data = $request->validated();
        if (empty($data['password'])) {
            unset($data['password']);
        }

        $this->service->update($mikrotik, $data);

        return response()->json(['success' => true, 'data' => $mikrotik->fresh()]);
    }

    public function toggleStatus(Request $request, Mikrotik $mikrotik)
    {
        $validated = $request->validate(['enabled' => ['required', 'boolean']]);
        $enabled = (bool) $validated['enabled'];

        $mikrotik->forceFill(array_filter([
            'status' => $enabled,
            'connected' => $enabled ? null : false,
        ], fn ($value) => $value !== null))->save();

        ActivityLog::create([
            'user_id' => $request->user()->id,
            'action' => $enabled ? 'mikrotik_enabled' : 'mikrotik_disabled',
            'resource_type' => Mikrotik::class,
            'resource_id' => $mikrotik->id,
            'description' => ($enabled ? 'Enabled' : 'Disabled').' MikroTik server: '.$mikrotik->name,
            'metadata' => ['ip_address' => $mikrotik->ip_address],
        ]);

        if ($enabled) {
            TestMikrotikConnectionJob::dispatch($mikrotik->id);
        }

        Cache::forget('mikrotik:dashboard-counts');

        return response()->json([
            'success' => true,
            'message' => 'MikroTik server '.($enabled ? 'enabled' : 'disabled').' successfully.',
        ]);
    }

    public function destroy(Mikrotik $mikrotik)
    {
        $this->service->delete($mikrotik);

        return response()->json(['success' => true]);
    }
}
