<?php

namespace App\Services\ESign;

use App\Mail\EsignRequestMail;
use App\Models\ESign\EsignAuditLog;
use App\Models\ESign\EsignContract;
use App\Models\ESign\EsignSignField;
use App\Models\ESign\EsignSigner;
use App\Services\Service;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

class EsignContractService extends Service
{
    public function __construct(
        private EsignAuditService $auditService,
        private EsignPdfService $pdfService,
    ) {}

    public function list(array $params): LengthAwarePaginator
    {
        $query = EsignContract::query()
            ->with(['signers:id,contract_id,name,email,role,status,signed_at', 'creator:id,name'])
            ->orderBy('created_at', 'desc');

        if (! empty($params['status'])) {
            $query->where('status', $params['status']);
        }

        if (! empty($params['search'])) {
            $search = $params['search'];
            $query->where(function ($q) use ($search) {
                $q->where('title', 'like', "%{$search}%")
                    ->orWhere('contract_code', 'like', "%{$search}%");
            });
        }

        if (! empty($params['date_from'])) {
            $query->whereDate('created_at', '>=', $params['date_from']);
        }

        if (! empty($params['date_to'])) {
            $query->whereDate('created_at', '<=', $params['date_to']);
        }

        $perPage = $params['per_page'] ?? 20;

        return $query->paginate($perPage);
    }

    public function stats(): array
    {
        $tenantId = $this->tenantId();

        $counts = EsignContract::where('tenant_id', $tenantId)
            ->selectRaw('status, COUNT(*) as count')
            ->groupBy('status')
            ->pluck('count', 'status')
            ->toArray();

        return [
            'total' => array_sum($counts),
            'draft' => $counts[EsignContract::STATUS_DRAFT] ?? 0,
            'pending' => $counts[EsignContract::STATUS_PENDING] ?? 0,
            'partially_signed' => $counts[EsignContract::STATUS_PARTIALLY_SIGNED] ?? 0,
            'completed' => $counts[EsignContract::STATUS_COMPLETED] ?? 0,
            'expired' => $counts[EsignContract::STATUS_EXPIRED] ?? 0,
            'cancelled' => $counts[EsignContract::STATUS_CANCELLED] ?? 0,
            'rejected' => $counts[EsignContract::STATUS_REJECTED] ?? 0,
        ];
    }

    public function create(array $data): EsignContract
    {
        $tenantId = $this->tenantId();
        $userId = $this->apiUserId();

        return DB::transaction(function () use ($data, $tenantId, $userId) {
            // PDF 파일 저장
            $file = $data['file'];
            $filePath = $file->store("esign/{$tenantId}/originals", 'local');
            $fileHash = hash_file('sha256', $file->getRealPath());

            // 계약 코드 생성
            $contractCode = 'ES-' . now()->format('Ymd') . '-' . strtoupper(Str::random(6));

            // 서명 순서 설정
            $signOrderType = $data['sign_order_type'] ?? EsignContract::SIGN_ORDER_COUNTERPART_FIRST;

            $contract = EsignContract::create([
                'tenant_id' => $tenantId,
                'contract_code' => $contractCode,
                'title' => $data['title'],
                'description' => $data['description'] ?? null,
                'sign_order_type' => $signOrderType,
                'original_file_path' => $filePath,
                'original_file_name' => $file->getClientOriginalName(),
                'original_file_hash' => $fileHash,
                'original_file_size' => $file->getSize(),
                'status' => EsignContract::STATUS_DRAFT,
                'expires_at' => $data['expires_at'] ?? now()->addDays(14),
                'created_by' => $userId,
                'updated_by' => $userId,
            ]);

            // 서명자 생성 - 작성자 (creator)
            $creatorOrder = $signOrderType === EsignContract::SIGN_ORDER_CREATOR_FIRST ? 1 : 2;
            $counterpartOrder = $signOrderType === EsignContract::SIGN_ORDER_CREATOR_FIRST ? 2 : 1;

            EsignSigner::create([
                'tenant_id' => $tenantId,
                'contract_id' => $contract->id,
                'role' => EsignSigner::ROLE_CREATOR,
                'sign_order' => $creatorOrder,
                'name' => $data['creator_name'],
                'email' => $data['creator_email'],
                'phone' => $data['creator_phone'] ?? null,
                'access_token' => Str::random(128),
                'token_expires_at' => $contract->expires_at,
                'status' => EsignSigner::STATUS_WAITING,
            ]);

            // 서명자 생성 - 상대방 (counterpart)
            EsignSigner::create([
                'tenant_id' => $tenantId,
                'contract_id' => $contract->id,
                'role' => EsignSigner::ROLE_COUNTERPART,
                'sign_order' => $counterpartOrder,
                'name' => $data['counterpart_name'],
                'email' => $data['counterpart_email'],
                'phone' => $data['counterpart_phone'] ?? null,
                'access_token' => Str::random(128),
                'token_expires_at' => $contract->expires_at,
                'status' => EsignSigner::STATUS_WAITING,
            ]);

            $this->auditService->log($contract->id, EsignAuditLog::ACTION_CREATED);

            return $contract->fresh(['signers', 'creator:id,name']);
        });
    }

    public function show(int $id): EsignContract
    {
        $contract = EsignContract::with([
            'signers',
            'signFields',
            'auditLogs' => fn ($q) => $q->orderBy('created_at', 'desc'),
            'auditLogs.signer:id,name,email,role',
            'creator:id,name',
        ])->find($id);

        if (! $contract) {
            throw new NotFoundHttpException(__('error.not_found'));
        }

        return $contract;
    }

    public function cancel(int $id): EsignContract
    {
        $userId = $this->apiUserId();

        return DB::transaction(function () use ($id, $userId) {
            $contract = EsignContract::find($id);

            if (! $contract) {
                throw new NotFoundHttpException(__('error.not_found'));
            }

            if ($contract->status === EsignContract::STATUS_COMPLETED) {
                throw new BadRequestHttpException(__('error.esign.already_completed'));
            }

            if ($contract->status === EsignContract::STATUS_CANCELLED) {
                throw new BadRequestHttpException(__('error.esign.already_cancelled'));
            }

            $contract->update([
                'status' => EsignContract::STATUS_CANCELLED,
                'updated_by' => $userId,
            ]);

            $this->auditService->log($contract->id, EsignAuditLog::ACTION_CANCELLED);

            return $contract->fresh(['signers', 'creator:id,name']);
        });
    }

    public function send(int $id): EsignContract
    {
        $userId = $this->apiUserId();

        return DB::transaction(function () use ($id, $userId) {
            $contract = EsignContract::with('signers')->find($id);

            if (! $contract) {
                throw new NotFoundHttpException(__('error.not_found'));
            }

            if (! in_array($contract->status, [EsignContract::STATUS_DRAFT])) {
                throw new BadRequestHttpException(__('error.esign.invalid_status_for_send'));
            }

            // 서명 필드가 설정되어 있는지 확인
            $fieldsCount = EsignSignField::where('contract_id', $contract->id)->count();
            if ($fieldsCount === 0) {
                throw new BadRequestHttpException(__('error.esign.no_sign_fields'));
            }

            $contract->update([
                'status' => EsignContract::STATUS_PENDING,
                'updated_by' => $userId,
            ]);

            // 첫 번째 서명자에게 알림 발송
            $nextSigner = $contract->getNextSigner();
            if ($nextSigner) {
                $nextSigner->update(['status' => EsignSigner::STATUS_NOTIFIED]);

                Mail::to($nextSigner->email)->queue(
                    new EsignRequestMail($contract, $nextSigner)
                );
            }

            $this->auditService->log($contract->id, EsignAuditLog::ACTION_SENT);

            return $contract->fresh(['signers', 'creator:id,name']);
        });
    }

    public function remind(int $id): EsignContract
    {
        $contract = EsignContract::with('signers')->find($id);

        if (! $contract) {
            throw new NotFoundHttpException(__('error.not_found'));
        }

        if (! $contract->canSign()) {
            throw new BadRequestHttpException(__('error.esign.cannot_remind'));
        }

        $nextSigner = $contract->getNextSigner();
        if ($nextSigner) {
            Mail::to($nextSigner->email)->queue(
                new EsignRequestMail($contract, $nextSigner)
            );
        }

        $this->auditService->log($contract->id, EsignAuditLog::ACTION_REMINDED);

        return $contract;
    }

    public function configureFields(int $id, array $fields): EsignContract
    {
        return DB::transaction(function () use ($id, $fields) {
            $contract = EsignContract::with('signers')->find($id);

            if (! $contract) {
                throw new NotFoundHttpException(__('error.not_found'));
            }

            if ($contract->status !== EsignContract::STATUS_DRAFT) {
                throw new BadRequestHttpException(__('error.esign.fields_only_in_draft'));
            }

            // 기존 필드 삭제 후 재생성
            EsignSignField::where('contract_id', $contract->id)->delete();

            foreach ($fields as $field) {
                EsignSignField::create([
                    'tenant_id' => $contract->tenant_id,
                    'contract_id' => $contract->id,
                    'signer_id' => $field['signer_id'],
                    'page_number' => $field['page_number'],
                    'position_x' => $field['position_x'],
                    'position_y' => $field['position_y'],
                    'width' => $field['width'],
                    'height' => $field['height'],
                    'field_type' => $field['field_type'] ?? EsignSignField::TYPE_SIGNATURE,
                    'field_label' => $field['field_label'] ?? null,
                    'is_required' => $field['is_required'] ?? true,
                    'sort_order' => $field['sort_order'] ?? 0,
                ]);
            }

            return $contract->fresh(['signers', 'signFields']);
        });
    }
}
