<?php

namespace App\Http\Controllers\Web;

use App\Http\Controllers\Controller;
use App\Models\AttendancePunch;
use App\Models\BiometricDevice;
use App\Models\Company;
use App\Models\Location;
use App\Services\Biometric\DeviceMonthlyPunchSyncService;
use App\Support\FormTextRules;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Illuminate\Validation\Rule;

class BiometricDeviceController extends Controller
{
    public function index()
    {
        $devices = BiometricDevice::query()
            ->with('location')
            ->orderByDesc('id')
            ->paginate(20);

        return view('biometric-devices.index', compact('devices'));
    }

    public function create()
    {
        $locations = Location::query()->orderBy('name')->get();

        return view('biometric-devices.create', compact('locations'));
    }

    public function store(Request $request)
    {
        $companyId = (int) auth()->user()->company_id;
        $company = Company::withoutGlobalScopes()->findOrFail($companyId);

        $validated = $request->validate([
            'terminal_sn' => [
                'required',
                'string',
                'max:191',
                Rule::unique('biometric_devices', 'terminal_sn'),
            ],
            'terminal_alias' => FormTextRules::notNumericOnly(false, 191),
            'location_id' => [
                'nullable',
                'integer',
                Rule::exists('locations', 'id')->where(fn ($q) => $q->where('company_id', $companyId)),
            ],
            'is_active' => 'nullable|boolean',
            'punch_mode' => ['required', 'string', Rule::in(BiometricDevice::punchModes())],
            'allowed_ips' => 'nullable|string|max:2000',
        ], FormTextRules::messages());

        $plainPassword = BiometricDevice::generateApiPassword();
        $username = BiometricDevice::generateApiUsername($company);

        $device = BiometricDevice::create([
            'company_id' => $companyId,
            'terminal_sn' => trim($validated['terminal_sn']),
            'terminal_alias' => $validated['terminal_alias'] ?? null,
            'location_id' => $validated['location_id'] ?? null,
            'api_username' => $username,
            'api_password_hash' => Hash::make($plainPassword),
            'is_active' => $request->boolean('is_active', true),
            'punch_mode' => $validated['punch_mode'],
            'allowed_ips' => $this->parseIpList($validated['allowed_ips'] ?? null),
        ]);

        return redirect()
            ->route('biometric-devices.show', $device)
            ->with('success', 'Device registered. Copy the API credentials into the ZKTeco Api Settings form.')
            ->with('api_password_plain', $plainPassword);
    }

    public function show(BiometricDevice $biometricDevice)
    {
        $this->assertCompany($biometricDevice);
        $biometricDevice->load('location');

        $webhookUrl = url('/api/integrations/zkteco/attendance');
        $dataTemplate = config('zkteco.data_template');

        $month = request()->filled('month')
            ? Carbon::createFromFormat('Y-m', (string) request('month'))->startOfMonth()
            : now()->startOfMonth();

        $punchStats = AttendancePunch::query()
            ->where('biometric_device_id', $biometricDevice->id)
            ->whereBetween('punch_datetime', [
                $month->copy()->startOfMonth()->format('Y-m-d 00:00:00'),
                $month->copy()->endOfMonth()->format('Y-m-d 23:59:59'),
            ])
            ->selectRaw('status, count(*) as c')
            ->groupBy('status')
            ->pluck('c', 'status');

        $recentPunches = AttendancePunch::query()
            ->where('biometric_device_id', $biometricDevice->id)
            ->orderByDesc('punch_datetime')
            ->orderByDesc('id')
            ->limit(12)
            ->get();

        return view('biometric-devices.show', [
            'device' => $biometricDevice,
            'webhookUrl' => $webhookUrl,
            'dataTemplate' => $dataTemplate,
            'apiPasswordPlain' => session('api_password_plain'),
            'syncMonth' => $month->format('Y-m'),
            'punchStats' => $punchStats,
            'recentPunches' => $recentPunches,
        ]);
    }

    public function edit(BiometricDevice $biometricDevice)
    {
        $this->assertCompany($biometricDevice);
        $locations = Location::query()->orderBy('name')->get();

        return view('biometric-devices.edit', [
            'device' => $biometricDevice,
            'locations' => $locations,
        ]);
    }

    public function update(Request $request, BiometricDevice $biometricDevice)
    {
        $this->assertCompany($biometricDevice);
        $companyId = (int) auth()->user()->company_id;

        $validated = $request->validate([
            'terminal_sn' => [
                'required',
                'string',
                'max:191',
                Rule::unique('biometric_devices', 'terminal_sn')->ignore($biometricDevice->id),
            ],
            'terminal_alias' => FormTextRules::notNumericOnly(false, 191),
            'location_id' => [
                'nullable',
                'integer',
                Rule::exists('locations', 'id')->where(fn ($q) => $q->where('company_id', $companyId)),
            ],
            'is_active' => 'nullable|boolean',
            'punch_mode' => ['required', 'string', Rule::in(BiometricDevice::punchModes())],
            'allowed_ips' => 'nullable|string|max:2000',
        ], FormTextRules::messages());

        $biometricDevice->update([
            'terminal_sn' => trim($validated['terminal_sn']),
            'terminal_alias' => $validated['terminal_alias'] ?? null,
            'location_id' => $validated['location_id'] ?? null,
            'is_active' => $request->boolean('is_active', true),
            'punch_mode' => $validated['punch_mode'],
            'allowed_ips' => $this->parseIpList($validated['allowed_ips'] ?? null),
        ]);

        return redirect()
            ->route('biometric-devices.show', $biometricDevice)
            ->with('success', 'Device updated.');
    }

    public function destroy(BiometricDevice $biometricDevice)
    {
        $this->assertCompany($biometricDevice);
        $biometricDevice->delete();

        return redirect()
            ->route('biometric-devices.index')
            ->with('success', 'Device removed.');
    }

    public function regenerateCredentials(BiometricDevice $biometricDevice)
    {
        $this->assertCompany($biometricDevice);
        $company = Company::withoutGlobalScopes()->findOrFail((int) auth()->user()->company_id);

        $plainPassword = BiometricDevice::generateApiPassword();
        $biometricDevice->update([
            'api_username' => BiometricDevice::generateApiUsername($company),
            'api_password_hash' => Hash::make($plainPassword),
        ]);

        return redirect()
            ->route('biometric-devices.show', $biometricDevice)
            ->with('success', 'API credentials regenerated. Update the device Api Settings immediately.')
            ->with('api_password_plain', $plainPassword);
    }

    public function syncMonth(
        Request $request,
        BiometricDevice $biometricDevice,
        DeviceMonthlyPunchSyncService $syncService
    ) {
        $this->assertCompany($biometricDevice);

        $validated = $request->validate([
            'month' => ['required', 'date_format:Y-m'],
        ]);

        $month = Carbon::createFromFormat('Y-m', $validated['month'])->startOfMonth();
        $summary = $syncService->syncMonth($biometricDevice, $month);

        $message = sprintf(
            'Month %s synced: %d punch(es) reprocessed — applied %d, ignored %d, error %d.',
            $summary['month'],
            $summary['total'],
            $summary['applied'],
            $summary['ignored'],
            $summary['error']
        );

        if ($summary['total'] === 0) {
            $message = sprintf(
                'No pending/error/ignored punches found for %s on this device.',
                $summary['month']
            );
        }

        return redirect()
            ->route('biometric-devices.show', [
                'biometricDevice' => $biometricDevice,
                'month' => $summary['month'],
            ])
            ->with('success', $message)
            ->with('sync_summary', $summary);
    }

    protected function assertCompany(BiometricDevice $device): void
    {
        if ((int) $device->company_id !== (int) auth()->user()->company_id) {
            abort(404);
        }
    }

    /**
     * @return list<string>|null
     */
    protected function parseIpList(?string $raw): ?array
    {
        if ($raw === null || trim($raw) === '') {
            return null;
        }

        $ips = preg_split('/[\s,;]+/', $raw) ?: [];
        $ips = array_values(array_filter(array_map('trim', $ips)));

        return count($ips) ? $ips : null;
    }
}
