<?php

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use App\Models\Datmas\Siswa;
use App\Models\Saving\Tsisreqdata;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\ValidationException;

class MobileStudentProfileController extends Controller
{
    public function show(Request $request): JsonResponse
    {
        /** @var Siswa $siswa */
        $siswa = $request->attributes->get('auth_siswa');
        $siswa->loadMissing(['safe', 'kelsis', 'indentitas.registrasi']);

        return response()->json([
            'success' => true,
            'message' => 'Data siswa berhasil diambil.',
            'data' => [
                'profile' => $this->profilePayload($siswa),
                'pending_request' => $this->latestPendingRequest($siswa),
            ],
        ]);
    }

    public function update(Request $request): JsonResponse
    {
        $validated = $request->validate($this->combinedRules());
        $changes = [];

        if (array_key_exists('tel', $validated)) {
            $changes['tel'] = formatPhoneNumber((string) $validated['tel']);
        }

        if (array_key_exists('email', $validated)) {
            $changes['email'] = $validated['email'];
        }

        if (array_key_exists('sakit', $validated)) {
            $changes['alamat']['sakit'] = $this->normalizeHealthNotes($validated['sakit']);
        }

        if (array_key_exists('alamat', $validated)) {
            $address = $this->normalizeAddressInput($validated['alamat']);

            if (! empty($address)) {
                $changes['alamat'] = array_merge($changes['alamat'] ?? [], $address);
            }
        }

        return $this->storeChangeRequest($request, $changes);
    }

    public function updateContact(Request $request): JsonResponse
    {
        $validated = $request->validate([
            'tel' => ['sometimes', 'required', 'string', 'max:20'],
            'email' => ['sometimes', 'nullable', 'email:rfc', 'max:100'],
        ]);

        $changes = [];

        if (array_key_exists('tel', $validated)) {
            $changes['tel'] = formatPhoneNumber((string) $validated['tel']);
        }

        if (array_key_exists('email', $validated)) {
            $changes['email'] = $validated['email'];
        }

        return $this->storeChangeRequest($request, $changes);
    }

    public function updateHealthNotes(Request $request): JsonResponse
    {
        $validated = $request->validate([
            'sakit' => ['present', 'array', 'max:10'],
            'sakit.*' => ['nullable', 'string', 'max:100'],
        ]);

        return $this->storeChangeRequest($request, [
            'alamat' => [
                'sakit' => $this->normalizeHealthNotes($validated['sakit']),
            ],
        ]);
    }

    public function updateAddress(Request $request): JsonResponse
    {
        $validated = $request->validate($this->addressRules(required: true));

        return $this->storeChangeRequest($request, [
            'alamat' => $this->normalizeAddressInput($validated['alamat']),
        ]);
    }

    protected function storeChangeRequest(Request $request, array $changes): JsonResponse
    {
        /** @var Siswa $siswa */
        $siswa = $request->attributes->get('auth_siswa');
        $siswa->loadMissing(['safe', 'indentitas.registrasi']);

        $changes = $this->removeEmptyTopLevelBranches($changes);

        if (empty($changes)) {
            throw ValidationException::withMessages([
                'payload' => ['Minimal kirim satu data yang akan diubah.'],
            ]);
        }

        $current = $this->currentComparableData($siswa);
        $oldData = [];
        $newData = [];

        foreach ($changes as $key => $value) {
            if ($key === 'alamat' && is_array($value)) {
                foreach ($value as $addressKey => $addressValue) {
                    $oldValue = data_get($current, "alamat.$addressKey");

                    if ($this->valuesAreDifferent($oldValue, $addressValue)) {
                        $oldData['alamat'][$addressKey] = $oldValue;
                        $newData['alamat'][$addressKey] = $addressValue;
                    }
                }

                continue;
            }

            $oldValue = data_get($current, $key);

            if ($this->valuesAreDifferent($oldValue, $value)) {
                $oldData[$key] = $oldValue;
                $newData[$key] = $value;
            }
        }

        if (empty($newData)) {
            throw ValidationException::withMessages([
                'payload' => ['Tidak ada perubahan data.'],
            ]);
        }

        $changeRequest = DB::connection('mai4')->transaction(fn () => Tsisreqdata::create([
            'idsis' => $siswa->id,
            'old_data' => $oldData,
            'new_data' => $newData,
            'status' => 0,
        ]));

        return response()->json([
            'success' => true,
            'message' => 'Permintaan perubahan data berhasil dikirim dan menunggu verifikasi.',
            'data' => [
                'request_id' => $changeRequest->id,
                'status' => 'pending',
                'old_data' => $oldData,
                'new_data' => $newData,
            ],
        ], 201);
    }

    protected function profilePayload(Siswa $siswa): array
    {
        $safe = $siswa->safe;
        $registration = $siswa->indentitas?->registrasi;

        return [
            'identity' => [
                'account_id' => $siswa->indentitas?->nouid ?: $siswa->nis,
                'nouid' => $siswa->indentitas?->nouid,
                'nis' => $siswa->nis,
                'nisn' => $siswa->nisn,
                'nama' => $siswa->namlen,
                'nama_panggilan' => $siswa->nampan,
                'kelas' => data_get($siswa->kelsis, 'kel') ?? $siswa->getRawOriginal('kel'),
                'tempat_lahir' => $siswa->temlah,
                'tanggal_lahir' => $siswa->tgllah?->format('Y-m-d'),
                'ttl' => $siswa->ttl,
                'jenis_kelamin' => $siswa->jenkel,
            ],
            'contact' => [
                'tel' => $siswa->tel,
                'email' => $registration?->email,
            ],
            'health' => [
                'sakit' => $this->normalizeHealthNotes($safe?->sakit),
            ],
            'address' => [
                'ala' => $safe?->ala ?? $siswa->ala,
                'rt' => $safe?->rt,
                'rw' => $safe?->rw,
                'cam' => $safe?->cam,
                'lur' => $safe?->lur,
                'kodpos' => $safe?->kodpos,
                'dusun' => $safe?->dusun,
                'temtin' => $safe?->temtin,
                'wilayah' => $safe?->wilayah,
                'kecamatan' => $safe?->kec,
                'desa' => $safe?->desa,
            ],
        ];
    }

    protected function currentComparableData(Siswa $siswa): array
    {
        $safe = $siswa->safe;

        return [
            'tel' => $this->safeFormatPhone($siswa->tel),
            'email' => $siswa->indentitas?->registrasi?->email,
            'alamat' => [
                'ala' => $safe?->ala ?? $siswa->ala,
                'rt' => $safe?->rt,
                'rw' => $safe?->rw,
                'cam' => $safe?->cam,
                'lur' => $safe?->lur,
                'kodpos' => $safe?->kodpos,
                'dusun' => $safe?->dusun,
                'temtin' => $safe?->temtin,
                'sakit' => $this->normalizeHealthNotes($safe?->sakit),
            ],
        ];
    }

    protected function latestPendingRequest(Siswa $siswa): ?array
    {
        $changeRequest = Tsisreqdata::query()
            ->where('idsis', $siswa->id)
            ->where('status', 0)
            ->latest('created_at')
            ->first();

        if (! $changeRequest) {
            return null;
        }

        return [
            'request_id' => $changeRequest->id,
            'status' => 'pending',
            'old_data' => $changeRequest->old_data,
            'new_data' => $changeRequest->new_data,
            'created_at' => $changeRequest->created_at?->toIso8601String(),
        ];
    }

    protected function combinedRules(): array
    {
        return [
            'tel' => ['sometimes', 'required', 'string', 'max:20'],
            'email' => ['sometimes', 'nullable', 'email:rfc', 'max:100'],
            'sakit' => ['sometimes', 'array', 'max:10'],
            'sakit.*' => ['nullable', 'string', 'max:100'],
            ...$this->addressRules(required: false),
        ];
    }

    protected function addressRules(bool $required): array
    {
        $addressRule = $required ? ['required', 'array'] : ['sometimes', 'array'];

        return [
            'alamat' => $addressRule,
            'alamat.ala' => ['sometimes', 'required', 'string', 'max:255'],
            'alamat.rt' => ['sometimes', 'nullable', 'string', 'max:10'],
            'alamat.rw' => ['sometimes', 'nullable', 'string', 'max:10'],
            'alamat.cam' => ['sometimes', 'nullable', 'string', 'max:20', 'exists:mai2.twilayah,kod'],
            'alamat.kec' => ['sometimes', 'nullable', 'string', 'max:20', 'exists:mai2.twilayah,kod'],
            'alamat.lur' => ['sometimes', 'nullable', 'string', 'max:20', 'exists:mai2.twilayah,kod'],
            'alamat.desa' => ['sometimes', 'nullable', 'string', 'max:20', 'exists:mai2.twilayah,kod'],
            'alamat.kodpos' => ['sometimes', 'nullable', 'string', 'max:10'],
            'alamat.dusun' => ['sometimes', 'nullable', 'string', 'max:20'],
            'alamat.temtin' => ['sometimes', 'nullable', 'string', 'max:10'],
            'alamat.wilayah' => ['sometimes', 'array'],
            'alamat.wilayah.kec' => ['sometimes', 'nullable', 'string', 'max:20', 'exists:mai2.twilayah,kod'],
            'alamat.wilayah.kel' => ['sometimes', 'nullable', 'string', 'max:20', 'exists:mai2.twilayah,kod'],
        ];
    }

    protected function normalizeAddressInput(array $address): array
    {
        if (array_key_exists('kec', $address)) {
            $address['cam'] = $address['kec'];
        }

        if (array_key_exists('desa', $address)) {
            $address['lur'] = $address['desa'];
        }

        if (array_key_exists('wilayah', $address) && is_array($address['wilayah'])) {
            if (array_key_exists('kec', $address['wilayah'])) {
                $address['cam'] = $address['wilayah']['kec'];
            }

            if (array_key_exists('kel', $address['wilayah'])) {
                $address['lur'] = $address['wilayah']['kel'];
            }
        }

        unset($address['kec'], $address['desa'], $address['wilayah']);

        return array_intersect_key($address, array_flip([
            'ala',
            'rt',
            'rw',
            'cam',
            'lur',
            'kodpos',
            'dusun',
            'temtin',
        ]));
    }

    protected function normalizeHealthNotes(mixed $notes): array
    {
        if ($notes === null || $notes === '') {
            return [];
        }

        if (is_string($notes)) {
            $decoded = json_decode($notes, true);
            $notes = is_array($decoded) ? $decoded : preg_split('/\r\n|\r|\n|,/', $notes);
        }

        if (! is_array($notes)) {
            return [];
        }

        return collect($notes)
            ->map(fn ($note) => trim((string) $note))
            ->filter(fn ($note) => $note !== '')
            ->values()
            ->all();
    }

    protected function removeEmptyTopLevelBranches(array $data): array
    {
        foreach ($data as $key => $value) {
            if (is_array($value) && $value === []) {
                unset($data[$key]);
            }
        }

        return $data;
    }

    protected function safeFormatPhone(?string $phone): ?string
    {
        if (! $phone) {
            return null;
        }

        try {
            return formatPhoneNumber($phone);
        } catch (ValidationException) {
            return $phone;
        }
    }

    protected function valuesAreDifferent(mixed $oldValue, mixed $newValue): bool
    {
        if (is_array($oldValue) || is_array($newValue)) {
            return $oldValue !== $newValue;
        }

        return (string) $oldValue !== (string) $newValue;
    }
}
