<?php

namespace App\Services\Admin;

use App\Repositories\Admin\AdminUserRepository;
use Illuminate\Http\Request;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Crypt;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
use Illuminate\Validation\Rule;

final class AdminMeService
{
    public function __construct(
        private readonly AdminUserRepository $repo,
        private readonly AdminAuditService $audit,
    ) {}

    public function decryptPhoneForMe($adminUser): string
    {
        $enc = (string) ($adminUser->phone_enc ?? '');
        if ($enc === '') return '';

        try {
            // 최근 방식(encryptString) 우선
            $v = Crypt::decryptString($enc);
        } catch (\Throwable) {
            try {
                // 과거 방식(encrypt) 호환: decrypt()는 unserialize까지 해줌
                $v = Crypt::decrypt($enc);
            } catch (\Throwable) {
                return '';
            }
        }

        // decryptString으로 풀렸는데 serialized string이면 안전하게 한 번 풀기
        if (is_string($v) && preg_match('/^s:\d+:"/', $v)) {
            try {
                $u = @unserialize($v, ['allowed_classes' => false]);
                if (is_string($u)) return $u;
            } catch (\Throwable) {
                // ignore
            }
        }

        return is_string($v) ? $v : '';
    }

    public function updateProfile(int $adminId, Request $request): array
    {
        $phone = trim((string) $request->input('phone', ''));

        $data = $request->validate([
            'nickname' => ['required', 'string', 'min:2', 'max:80'],  // ✅ 닉네임(예: super admin)
            'name'     => ['required', 'string', 'min:2', 'max:80'],  // ✅ 성명(본명)
            'phone'    => ['nullable', 'string', 'max:30'],
        ]);

        $me = $this->repo->find($adminId);
        if (!$me) {
            return ['ok' => false, 'errors' => ['common' => '사용자 정보를 찾을 수 없습니다.']];
        }

        $before = [
            'nickname' => (string)($me->nickname ?? ''),
            'name' => (string)($me->name ?? ''),
            'phone_last4' => $this->last4($this->decryptPhoneForMe($me)),
        ];

        $normalized = '';
        $phoneHash = null;
        $phoneEnc = null;

        if ($phone !== '') {
            $normalized = $this->normalizeKoreanPhone($phone);
            if ($normalized === '') {
                return ['ok' => false, 'errors' => ['phone' => '휴대폰 번호 형식이 올바르지 않습니다.']];
            }

            $phoneHash = $this->makePhoneHash($normalized);
            $phoneEnc  = \Illuminate\Support\Facades\Crypt::encryptString($normalized);

            if ($this->repo->existsPhoneHash($phoneHash, $adminId)) {
                return ['ok' => false, 'errors' => ['phone' => '이미 사용 중인 휴대폰 번호입니다.']];
            }
        }

        $payload = [
            'nickname' => (string)$data['nickname'],
            'name'     => (string)$data['name'],
            'phone_enc' => $phoneEnc,
            'phone_hash' => $phoneHash,

            // ✅ updated_by 컬럼이 없어도 Repository가 자동 제거함
            'updated_by' => $adminId,
        ];

        return DB::transaction(function () use ($adminId, $payload, $request, $before) {
            $ok = $this->repo->updateById($adminId, $payload);
            if (!$ok) {
                return ['ok' => false, 'errors' => ['common' => '저장에 실패했습니다. 잠시 후 다시 시도해 주세요.']];
            }

            $after = [
                'nickname' => (string)$payload['nickname'],
                'name' => (string)$payload['name'],
                'phone_last4' => $this->last4($payload['phone_enc'] ? \Illuminate\Support\Facades\Crypt::decryptString($payload['phone_enc']) : ''),
            ];

            $this->audit->log(
                actorAdminId: $adminId,
                action: 'admin.me.update',
                targetType: 'admin_user',
                targetId: $adminId,
                before: $before,
                after: $after,
                ip: (string) $request->ip(),
                ua: (string) $request->userAgent(),
            );

            return ['ok' => true, 'message' => '내 정보가 변경되었습니다.'];
        });
    }

    public function changePassword(int $adminId, Request $request): array
    {
        $data = $request->validate([
            'current_password' => ['required', 'string'],
            'password' => ['required', 'string', 'min:10', 'max:72', 'confirmed'],
        ]);

        $me = $this->repo->find($adminId);
        if (!$me) {
            return ['ok' => false, 'errors' => ['common' => '사용자 정보를 찾을 수 없습니다.']];
        }

        if (!Hash::check((string)$data['current_password'], (string)$me->password)) {
            return ['ok' => false, 'errors' => ['current_password' => '현재 비밀번호가 일치하지 않습니다.']];
        }

        $before = [
            'password_changed_at' => (string)($me->password_changed_at ?? ''),
        ];

        return DB::transaction(function () use ($adminId, $data, $request, $before) {
            $hash = Hash::make((string)$data['password']);

            $ok = $this->repo->update($adminId, [
                'password' => $hash,
                'password_changed_at' => now(),
                'updated_by' => $adminId,
            ]);

            if (!$ok) {
                return ['ok' => false, 'errors' => ['common' => '비밀번호 변경에 실패했습니다.']];
            }

            $this->audit->log(
                actorAdminId: $adminId,
                action: 'admin.me.password.change',
                targetType: 'admin_user',
                targetId: $adminId,
                before: $before,
                after: ['password_changed_at' => (string)now()],
                ip: (string) $request->ip(),
                ua: (string) $request->userAgent(),
            );

            return ['ok' => true, 'message' => '비밀번호가 변경되었습니다.'];
        });
    }

    private function normalizeKoreanPhone(string $raw): string
    {
        $v = preg_replace('/\D+/', '', $raw ?? '');
        if (!$v) return '';

        // +82 / 82 처리
        if (str_starts_with($v, '82')) {
            $v = '0' . substr($v, 2);
        }

        // 010 / 011 등 최소 10~11자리만 허용(운영 정책에 맞게 조정 가능)
        if (!preg_match('/^0\d{9,10}$/', $v)) {
            return '';
        }
        return $v;
    }

    private function makePhoneHash(string $normalizedPhone): string
    {
        $key = (string) config('security.phone_hash_key', config('app.key'));

        // app.key가 base64:로 시작하면 decode
        if (str_starts_with($key, 'base64:')) {
            $key = base64_decode(substr($key, 7)) ?: $key;
        }

        return hash_hmac('sha256', $normalizedPhone, $key);
    }

    private function last4(string $phone): string
    {
        $p = preg_replace('/\D+/', '', $phone ?? '');
        if (strlen($p) < 4) return '';
        return substr($p, -4);
    }
}
