<?php

namespace App\Services;

use App\Models\Members\UserTenant;
use App\Models\Tenants\Tenant;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\Validator;

class TenantService
{
    /**
     * 한글 자음 배열
     */
    private const INITIALS = ['ㄱ', 'ㄲ', 'ㄴ', 'ㄷ', 'ㄸ', 'ㄹ', 'ㅁ', 'ㅂ', 'ㅃ', 'ㅅ', 'ㅆ', 'ㅇ', 'ㅈ', 'ㅉ', 'ㅊ', 'ㅋ', 'ㅌ', 'ㅍ', 'ㅎ'];

    /**
     * 대소문자를 구분하지 않는 36진수 문자열로 변경
     */
    private string $base36Chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';

    /**
     * 한글 업체명에서 초성 약어를 추출합니다.
     * 외부 라이브러리 없이 자체적으로 구현했습니다.
     */
    private function getInitials(string $tenantName): string
    {
        $initials = '';
        $charLength = mb_strlen($tenantName, 'UTF-8');

        for ($i = 0; $i < $charLength; $i++) {
            $char = mb_substr($tenantName, $i, 1, 'UTF-8');
            $code = mb_ord($char, 'UTF-8');

            // 한글 초성을 추출
            if ($code >= 0xAC00 && $code <= 0xD7A3) { // 한글 유니코드 범위
                $index = floor(($code - 0xAC00) / 588);
                $initials .= self::INITIALS[$index];
            }
            // 한글이 아닌 문자는 무시합니다.
        }

        $koreanInitials = ['ㄱ', 'ㄲ', 'ㄴ', 'ㄷ', 'ㄸ', 'ㄹ', 'ㅁ', 'ㅂ', 'ㅃ', 'ㅅ', 'ㅆ', 'ㅇ', 'ㅈ', 'ㅉ', 'ㅊ', 'ㅋ', 'ㅌ', 'ㅍ', 'ㅎ'];
        $englishInitials = ['G', 'KK', 'N', 'D', 'TT', 'R', 'M', 'B', 'BB', 'S', 'SS', 'O', 'J', 'JJ', 'CH', 'K', 'T', 'P', 'H'];
        $initials = strtr($initials, array_combine($koreanInitials, $englishInitials));

        $initials = str_replace(' ', '', $initials);

        return strtoupper($initials);
    }

    /**
     * 10진수 숫자를 4자리 36진수 문자열로 변환합니다.
     */
    private function toBase36(int $number): string
    {
        $result = '';
        $base = strlen($this->base36Chars);

        // **수정된 부분: 4자리 고정**
        for ($i = 0; $i < 4; $i++) {
            $remainder = $number % $base;
            $result = $this->base36Chars[$remainder].$result;
            $number = floor($number / $base);
        }

        return $result;
    }

    /**
     * 36진수 문자열을 10진수 숫자로 변환합니다.
     */
    private function fromBase36(string $base36String): int
    {
        $number = 0;
        $base = strlen($this->base36Chars);
        $len = strlen($base36String);

        for ($i = 0; $i < $len; $i++) {
            $char = $base36String[$i];
            $charValue = strpos($this->base36Chars, $char);

            // strpos가 false를 반환할 경우를 대비해 예외 처리
            if ($charValue === false) {
                return 0;
            }
            $number += $charValue * ($base ** ($len - 1 - $i));
        }

        return $number;
    }

    /**
     * 한글 업체명 기반으로 순환형 테넌트 코드를 생성합니다.
     */
    public function generateTenantCode(string $tenantName): string
    {
        $cleanTenantName = str_replace(['주식회사', '(주)', '유한회사', '(유)', '유한책임회사', '(유)책', '합명회사', '(합)', '합자회사', '(합자)'], '', $tenantName);

        // 1. 전처리된 업체명에서 초성 약어 생성
        $initials = $this->getInitials($cleanTenantName);

        // 2. 모든 테넌트들의 코드 중 가장 큰 순번을 찾습니다.
        $lastNumber = -1;
        $existingTenants = Tenant::all();

        foreach ($existingTenants as $tenant) {
            // **수정된 부분: 코드 마지막 4자리를 순번으로 간주**
            if (strlen($tenant->code) >= 4) {
                $sequenceString = substr($tenant->code, -4);

                // 마지막 4자리가 36진수 문자열인지 확인
                if (strspn($sequenceString, $this->base36Chars) === 4) {
                    $sequence = $this->fromBase36($sequenceString);
                    if ($sequence > $lastNumber) {
                        $lastNumber = $sequence;
                    }
                }
            }
        }

        // 3. 마지막 순번에 1을 더하고 36^4 (1,679,616)로 나눈 나머지로 순환하도록 유지합니다.
        // **수정된 부분: 46656 -> 1679616 으로 변경**
        $nextSequence = ($lastNumber + 1) % 1679616;

        // 4. 순번을 4자리 36진수 문자열로 포맷
        // **수정된 부분: toBase36 호출**
        $formattedSequence = $this->toBase36($nextSequence);

        // 5. 초성 약어와 순번을 조합하여 최종 코드 생성
        $code = $initials.$formattedSequence;

        return $code;
    }

    /**
     * 테넌트 목록 조회 (페이징)
     *
     * @param  array  $params  [page, size, search 등]
     */
    public static function getTenants(array $params = [])
    {
        $tenantId = $params['tenant_id'] ?? app('tenant_id');

        if (! $tenantId) {
            // 현재 사용자 기본 테넌트 조회
            $apiUser = app('api_user');
            $userTenant = UserTenant::where('user_id', $apiUser)
                ->where('is_default', 1)
                ->first();

            if (! $userTenant) {
                return ['error' => '활성(기본) 테넌트를 찾을 수 없습니다.', 'code' => 404];
            }
            $tenantId = $userTenant->tenant_id;
        }

        $pageNo = isset($params['page']) ? (int) $params['page'] : 1;
        $pageSize = isset($params['size']) ? (int) $params['size'] : 10;

        $query = Tenant::query();

        // (옵션) 간단 검색 예시: 회사명/코드
        if (! empty($params['q'])) {
            $q = trim($params['q']);
            $query->where(function ($qq) use ($q) {
                $qq->where('company_name', 'like', "%{$q}%")
                    ->orWhere('code', 'like', "%{$q}%")
                    ->orWhere('email', 'like', "%{$q}%");
            });
        }

        // (옵션) 정렬
        if (! empty($params['sort']) && in_array($params['sort'], ['company_name', 'code', 'created_at', 'updated_at'])) {
            $dir = (! empty($params['dir']) && in_array(strtolower($params['dir']), ['asc', 'desc'])) ? $params['dir'] : 'desc';
            $query->orderBy($params['sort'], $dir);
        } else {
            $query->orderByDesc('id');
        }

        $paginator = $query->paginate($pageSize, ['*'], 'page', $pageNo);

        return $paginator;
    }

    /**
     * 단일 테넌트 조회
     * - params.tenant_id 가 있으면 해당 테넌트
     * - 없으면 현재 사용자 기본(is_default=1) 테넌트
     *
     * @param  array  $params  [tenant_id]
     */
    public static function getTenant(array $params = [])
    {

        $tenantId = $params['tenant_id'] ?? app('tenant_id');

        if (! $tenantId) {
            // 현재 사용자 기본 테넌트 조회
            $apiUser = app('api_user');
            $userTenant = UserTenant::where('user_id', $apiUser)
                ->where('is_default', 1)
                ->first();

            if (! $userTenant) {
                return ['error' => '활성(기본) 테넌트를 찾을 수 없습니다.', 'code' => 404];
            }
            $tenantId = $userTenant->tenant_id;
        }

        // 필요한 컬럼만 선택 (원하면 조정)
        $query = Tenant::query()
            ->select('id', 'company_name', 'code', 'email', 'phone', 'address', 'business_num', 'corp_reg_no', 'ceo_name', 'homepage', 'fax', 'logo', 'admin_memo', 'options', 'created_at', 'updated_at')
            ->where('id', $tenantId);

        return $query->first();
    }

    /**
     * 테넌트 등록
     */
    public static function storeTenants(array $params = [])
    {

        $validator = Validator::make($params, [
            'company_name' => 'required|string|max:255',
            'email' => 'nullable|email|max:100',
            'phone' => 'nullable|string|max:30',
            'address' => 'nullable|string|max:255',
            'business_num' => 'nullable|string|max:30',
            'corp_reg_no' => 'nullable|string|max:30',
            'ceo_name' => 'nullable|string|max:100',
            'homepage' => 'nullable|string|max:255',
            'fax' => 'nullable|string|max:50',
            'logo' => 'nullable|string|max:255',
            'admin_memo' => 'nullable|string',
            'options' => 'nullable', // JSON 문자열 저장이라면 'nullable|json'
        ]);

        if ($validator->fails()) {
            return ['error' => $validator->errors()->first(), 'code' => 400];
        }

        $payload = $validator->validated();

        // TenantService 인스턴스를 가져옵니다.
        $tenantService = app(TenantService::class);

        // 업체명 기반으로 고유한 코드를 생성합니다.
        $code = $tenantService->generateTenantCode($payload['company_name']);

        // 생성된 코드를 페이로드에 추가합니다.
        $payload['code'] = $code;

        $tenant = Tenant::create($payload);

        // 기존 기본값(is_default=1) 해제
        $apiUser = app('api_user');
        UserTenant::withoutGlobalScopes()
            ->where('user_id', $apiUser)
            ->where('is_default', 1)
            ->update(['is_default' => 0]);

        // 성성된 테넌트를 나의 테넌트로 셋팅
        $apiUser = app('api_user');
        UserTenant::create([
            'user_id' => $apiUser,
            'tenant_id' => $tenant->id,
            'is_active' => 0,
            'is_default' => 1,
            'joined_at' => now(),
        ]);

        // 생성된 리소스를 그대로 반환 (목록 카드용 요약 원하면 컬럼 제한)
        return $tenant;
    }

    /**
     * 테넌트 수정
     */
    public static function updateTenant(array $params = [])
    {

        $validator = Validator::make($params, [
            'company_name' => 'sometimes|string|max:255',
            'email' => 'sometimes|nullable|email|max:100',
            'phone' => 'sometimes|nullable|string|max:30',
            'address' => 'sometimes|nullable|string|max:255',
            'business_num' => 'sometimes|nullable|string|max:30',
            'corp_reg_no' => 'sometimes|nullable|string|max:30',
            'ceo_name' => 'sometimes|nullable|string|max:100',
            'homepage' => 'sometimes|nullable|string|max:255',
            'fax' => 'sometimes|nullable|string|max:50',
            'logo' => 'sometimes|nullable|string|max:255',
            'admin_memo' => 'sometimes|nullable|string',
            'options' => 'sometimes|nullable', // JSON 문자열이면 'sometimes|nullable|json'
        ]);

        if ($validator->fails()) {
            return ['error' => $validator->errors()->first(), 'code' => 400];
        }

        $payload = $validator->validated();
        $tenantId = app('tenant_id') ?? null;
        unset($payload['tenant_id']);

        if (empty($payload)) {
            return ['error' => '수정할 데이터가 없습니다.', 'code' => 400];
        }

        $tenant = Tenant::find($tenantId);
        if (! $tenant) {
            return ['error' => '테넌트를 찾을 수 없습니다.', 'code' => 404];
        }

        $tenant->update($payload);

        return $tenant->fresh();
    }

    /**
     * 테넌트 삭제(탈퇴) — 소프트 삭제 가정
     *
     * @param  int  $tenant_id
     */
    public static function destroyTenant(array $params = [])
    {
        $tenantId = $params['tenant_id'] ?? app('tenant_id');
        if (! $tenantId) {
            return ['error' => 'tenant_id가 필요합니다.', 'code' => 400];
        }

        $tenant = Tenant::find($tenantId);
        if (! $tenant) {
            return ['error' => '테넌트를 찾을 수 없습니다.', 'code' => 404];
        }

        $tenant->delete(); // SoftDeletes 트레이트가 있으면 소프트 삭제

        return 'success';
    }

    /**
     * 테넌트 복구 (소프트 삭제된 레코드 대상)
     *
     * @param  array  $params  [tenant_id:int]
     */
    public static function restoreTenant(array $params = [])
    {
        $tenantId = $params['tenant_id'] ?? app('tenant_id');

        // 소프트 삭제 포함 조회
        $tenant = Tenant::withTrashed()->find($tenantId);
        if (! $tenant) {
            return ['error' => '테넌트를 찾을 수 없습니다.', 'code' => 404];
        }

        if (is_null($tenant->deleted_at)) {
            // 이미 활성 상태
            return ['error' => '이미 활성화된 테넌트입니다.', 'code' => 400];
        }

        $tenant->restore();

        // 복구 결과를 data에 담고 싶으면 fresh() 후 필요한 필드만 반환
        // return $tenant->fresh();

        return 'success';
    }

    /**
     * 테넌트 로고 업로드
     *
     * @param  UploadedFile  $file  업로드된 로고 이미지
     * @return array{logo_url: string, tenant: Tenant}
     */
    public static function uploadLogo(UploadedFile $file): array
    {
        $tenantId = app('tenant_id');

        if (! $tenantId) {
            throw new \Exception('테넌트 정보를 찾을 수 없습니다.');
        }

        $tenant = Tenant::find($tenantId);
        if (! $tenant) {
            throw new \Exception('테넌트를 찾을 수 없습니다.');
        }

        // 기존 로고 삭제
        if ($tenant->logo) {
            // logo 필드에 저장된 경로가 전체 URL인 경우 처리
            $oldPath = $tenant->logo;
            if (str_starts_with($oldPath, '/storage/')) {
                $oldPath = str_replace('/storage/', '', $oldPath);
            }
            if (Storage::disk('public')->exists($oldPath)) {
                Storage::disk('public')->delete($oldPath);
            }
        }

        // 새 로고 저장: /tenants/{tenant_id}/logo_{timestamp}.{ext}
        $extension = $file->getClientOriginalExtension();
        $filename = 'logo_'.time().'.'.$extension;
        $path = sprintf('tenants/%d/%s', $tenantId, $filename);

        Storage::disk('public')->putFileAs(
            dirname($path),
            $file,
            basename($path)
        );

        // 접근 가능한 URL 생성
        $logoUrl = '/storage/'.$path;

        // DB 업데이트
        $tenant->update(['logo' => $logoUrl]);

        return [
            'logo_url' => $logoUrl,
            'tenant' => $tenant->fresh(),
        ];
    }
}
