<?php

namespace App\Services;

use App\Models\Tenants\Sale;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Support\Facades\DB;

class SaleService extends Service
{
    /**
     * 매출 목록 조회
     */
    public function index(array $params): LengthAwarePaginator
    {
        $tenantId = $this->tenantId();

        $query = Sale::query()
            ->where('tenant_id', $tenantId)
            ->with(['client:id,name']);

        // 검색어 필터
        if (! empty($params['search'])) {
            $search = $params['search'];
            $query->where(function ($q) use ($search) {
                $q->where('sale_number', 'like', "%{$search}%")
                    ->orWhere('description', 'like', "%{$search}%")
                    ->orWhereHas('client', function ($q) use ($search) {
                        $q->where('name', 'like', "%{$search}%");
                    });
            });
        }

        // 날짜 범위 필터
        if (! empty($params['start_date'])) {
            $query->where('sale_date', '>=', $params['start_date']);
        }
        if (! empty($params['end_date'])) {
            $query->where('sale_date', '<=', $params['end_date']);
        }

        // 거래처 필터
        if (! empty($params['client_id'])) {
            $query->where('client_id', $params['client_id']);
        }

        // 상태 필터
        if (! empty($params['status'])) {
            $query->where('status', $params['status']);
        }

        // 정렬
        $sortBy = $params['sort_by'] ?? 'sale_date';
        $sortDir = $params['sort_dir'] ?? 'desc';
        $query->orderBy($sortBy, $sortDir);

        // 페이지네이션
        $perPage = $params['per_page'] ?? 20;

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

    /**
     * 매출 상세 조회
     */
    public function show(int $id): Sale
    {
        $tenantId = $this->tenantId();

        return Sale::query()
            ->where('tenant_id', $tenantId)
            ->with([
                'client:id,name',
                'deposit',
                'creator:id,name',
                'order.items' => function ($query) {
                    $query->select([
                        'id',
                        'order_id',
                        'item_name',
                        'quantity',
                        'unit_price',
                        'supply_amount',
                        'tax_amount',
                        'total_amount',
                        'note',
                        'sort_order',
                    ])->orderBy('sort_order');
                },
            ])
            ->findOrFail($id);
    }

    /**
     * 매출 등록
     */
    public function store(array $data): Sale
    {
        $tenantId = $this->tenantId();
        $userId = $this->apiUserId();

        return DB::transaction(function () use ($data, $tenantId, $userId) {
            // 매출번호 자동 생성
            $saleNumber = $this->generateSaleNumber($tenantId, $data['sale_date']);

            $sale = new Sale;
            $sale->tenant_id = $tenantId;
            $sale->sale_number = $saleNumber;
            $sale->sale_date = $data['sale_date'];
            $sale->client_id = $data['client_id'];
            $sale->supply_amount = $data['supply_amount'];
            $sale->tax_amount = $data['tax_amount'];
            $sale->total_amount = $data['total_amount'];
            $sale->description = $data['description'] ?? null;
            $sale->status = 'draft';
            $sale->deposit_id = $data['deposit_id'] ?? null;
            $sale->created_by = $userId;
            $sale->updated_by = $userId;
            $sale->save();

            return $sale->load(['client:id,name']);
        });
    }

    /**
     * 매출 수정
     */
    public function update(int $id, array $data): Sale
    {
        $tenantId = $this->tenantId();
        $userId = $this->apiUserId();

        return DB::transaction(function () use ($id, $data, $tenantId, $userId) {
            $sale = Sale::query()
                ->where('tenant_id', $tenantId)
                ->findOrFail($id);

            // 토글 필드만 업데이트하는 경우는 canEdit 체크 건너뛰기
            $toggleOnlyFields = ['tax_invoice_issued', 'transaction_statement_issued'];
            $isToggleOnly = count(array_diff(array_keys($data), $toggleOnlyFields)) === 0;

            // 확정 후에는 수정 불가 (토글 필드만 업데이트하는 경우 제외)
            if (! $isToggleOnly && ! $sale->canEdit()) {
                throw new \Exception(__('error.sale.cannot_edit'));
            }

            if (isset($data['sale_date'])) {
                $sale->sale_date = $data['sale_date'];
            }
            if (isset($data['client_id'])) {
                $sale->client_id = $data['client_id'];
            }
            if (isset($data['supply_amount'])) {
                $sale->supply_amount = $data['supply_amount'];
            }
            if (isset($data['tax_amount'])) {
                $sale->tax_amount = $data['tax_amount'];
            }
            if (isset($data['total_amount'])) {
                $sale->total_amount = $data['total_amount'];
            }
            if (array_key_exists('description', $data)) {
                $sale->description = $data['description'];
            }
            if (array_key_exists('deposit_id', $data)) {
                $sale->deposit_id = $data['deposit_id'];
            }
            if (array_key_exists('tax_invoice_issued', $data)) {
                $sale->tax_invoice_issued = $data['tax_invoice_issued'];
            }
            if (array_key_exists('transaction_statement_issued', $data)) {
                $sale->transaction_statement_issued = $data['transaction_statement_issued'];
            }

            $sale->updated_by = $userId;
            $sale->save();

            return $sale->fresh(['client:id,name']);
        });
    }

    /**
     * 매출 삭제
     */
    public function destroy(int $id): bool
    {
        $tenantId = $this->tenantId();
        $userId = $this->apiUserId();

        return DB::transaction(function () use ($id, $tenantId, $userId) {
            $sale = Sale::query()
                ->where('tenant_id', $tenantId)
                ->findOrFail($id);

            // 확정 후에는 삭제 불가
            if (! $sale->canDelete()) {
                throw new \Exception(__('error.sale.cannot_delete'));
            }

            $sale->deleted_by = $userId;
            $sale->save();
            $sale->delete();

            return true;
        });
    }

    /**
     * 매출 확정
     */
    public function confirm(int $id): Sale
    {
        $tenantId = $this->tenantId();
        $userId = $this->apiUserId();

        return DB::transaction(function () use ($id, $tenantId, $userId) {
            $sale = Sale::query()
                ->where('tenant_id', $tenantId)
                ->findOrFail($id);

            if (! $sale->canConfirm()) {
                throw new \Exception(__('error.sale.cannot_confirm'));
            }

            $sale->status = 'confirmed';
            $sale->updated_by = $userId;
            $sale->save();

            return $sale->fresh(['client:id,name']);
        });
    }

    /**
     * 매출 계정과목 일괄 변경
     *
     * @param  array  $ids  대상 ID 배열
     * @param  string  $accountCode  변경할 계정과목 코드
     * @return int 변경된 레코드 수
     */
    public function bulkUpdateAccountCode(array $ids, string $accountCode): int
    {
        $tenantId = $this->tenantId();
        $userId = $this->apiUserId();

        return Sale::query()
            ->where('tenant_id', $tenantId)
            ->whereIn('id', $ids)
            ->update([
                'account_code' => $accountCode,
                'updated_by' => $userId,
            ]);
    }

    /**
     * 매출 요약 (기간별 합계)
     */
    public function summary(array $params): array
    {
        $tenantId = $this->tenantId();

        $query = Sale::query()
            ->where('tenant_id', $tenantId);

        // 날짜 범위 필터
        if (! empty($params['start_date'])) {
            $query->where('sale_date', '>=', $params['start_date']);
        }
        if (! empty($params['end_date'])) {
            $query->where('sale_date', '<=', $params['end_date']);
        }

        // 거래처 필터
        if (! empty($params['client_id'])) {
            $query->where('client_id', $params['client_id']);
        }

        // 상태 필터
        if (! empty($params['status'])) {
            $query->where('status', $params['status']);
        }

        // 전체 합계
        $totalSupply = (clone $query)->sum('supply_amount');
        $totalTax = (clone $query)->sum('tax_amount');
        $totalAmount = (clone $query)->sum('total_amount');
        $count = (clone $query)->count();

        // 상태별 합계
        $byStatus = (clone $query)
            ->select('status', DB::raw('SUM(total_amount) as total'), DB::raw('COUNT(*) as count'))
            ->groupBy('status')
            ->get()
            ->keyBy('status')
            ->toArray();

        return [
            'total_supply_amount' => (float) $totalSupply,
            'total_tax_amount' => (float) $totalTax,
            'total_amount' => (float) $totalAmount,
            'total_count' => $count,
            'by_status' => $byStatus,
        ];
    }

    /**
     * 매출번호 자동 생성
     */
    private function generateSaleNumber(int $tenantId, string $saleDate): string
    {
        $prefix = 'SL'.date('Ymd', strtotime($saleDate));

        $lastSale = Sale::query()
            ->where('tenant_id', $tenantId)
            ->where('sale_number', 'like', $prefix.'%')
            ->orderBy('sale_number', 'desc')
            ->first();

        if ($lastSale) {
            $lastSeq = (int) substr($lastSale->sale_number, -4);
            $newSeq = $lastSeq + 1;
        } else {
            $newSeq = 1;
        }

        return $prefix.str_pad($newSeq, 4, '0', STR_PAD_LEFT);
    }

    /**
     * 거래명세서 조회
     */
    public function getStatement(int $id): array
    {
        $tenantId = $this->tenantId();

        $sale = Sale::query()
            ->where('tenant_id', $tenantId)
            ->with(['client', 'deposit', 'creator:id,name'])
            ->findOrFail($id);

        // 거래명세서 데이터 구성
        return [
            'statement_number' => $this->generateStatementNumber($sale),
            'issued_at' => $sale->statement_issued_at,
            'sale' => $sale,
            'seller' => $this->getSellerInfo($tenantId),
            'buyer' => $this->getBuyerInfo($sale->client),
            'items' => $this->getSaleItems($sale),
            'summary' => [
                'supply_amount' => $sale->supply_amount,
                'tax_amount' => $sale->tax_amount,
                'total_amount' => $sale->total_amount,
            ],
        ];
    }

    /**
     * 거래명세서 발행
     */
    public function issueStatement(int $id): array
    {
        $tenantId = $this->tenantId();
        $userId = $this->apiUserId();

        return DB::transaction(function () use ($id, $tenantId, $userId) {
            $sale = Sale::query()
                ->where('tenant_id', $tenantId)
                ->findOrFail($id);

            // 확정된 매출만 거래명세서 발행 가능
            if ($sale->status !== 'confirmed') {
                throw new \Exception(__('error.sale.statement_requires_confirmed'));
            }

            // 발행 시간 기록
            $sale->statement_issued_at = now();
            $sale->statement_issued_by = $userId;
            $sale->save();

            return [
                'statement_number' => $this->generateStatementNumber($sale),
                'issued_at' => $sale->statement_issued_at->toIso8601String(),
            ];
        });
    }

    /**
     * 거래명세서 일괄 발행
     *
     * @param  array<int>  $ids  발행할 매출 ID 배열
     * @return array{issued: int, failed: int, errors: array<int, string>}
     */
    public function bulkIssueStatement(array $ids): array
    {
        $tenantId = $this->tenantId();
        $userId = $this->apiUserId();
        $results = [
            'issued' => 0,
            'failed' => 0,
            'errors' => [],
        ];

        $sales = Sale::query()
            ->where('tenant_id', $tenantId)
            ->whereIn('id', $ids)
            ->get();

        foreach ($sales as $sale) {
            try {
                // 확정된 매출만 거래명세서 발행 가능
                if ($sale->status !== 'confirmed') {
                    $results['errors'][$sale->id] = __('error.sale.statement_requires_confirmed');
                    $results['failed']++;

                    continue;
                }

                // 이미 발행된 경우
                if ($sale->statement_issued_at !== null) {
                    $results['errors'][$sale->id] = __('error.sale.statement_already_issued');
                    $results['failed']++;

                    continue;
                }

                // 발행 시간 기록
                $sale->statement_issued_at = now();
                $sale->statement_issued_by = $userId;
                $sale->save();

                $results['issued']++;
            } catch (\Throwable $e) {
                $results['errors'][$sale->id] = $e->getMessage();
                $results['failed']++;
            }
        }

        // 요청된 ID 중 찾지 못한 것들도 실패 처리
        $foundIds = $sales->pluck('id')->toArray();
        $notFoundIds = array_diff($ids, $foundIds);
        foreach ($notFoundIds as $notFoundId) {
            $results['errors'][$notFoundId] = __('error.sale.not_found');
            $results['failed']++;
        }

        return $results;
    }

    /**
     * 거래명세서 이메일 발송
     */
    public function sendStatement(int $id, array $data): array
    {
        $tenantId = $this->tenantId();

        $sale = Sale::query()
            ->where('tenant_id', $tenantId)
            ->with(['client'])
            ->findOrFail($id);

        // 수신자 이메일 확인
        $recipientEmail = $data['email'] ?? $sale->client?->email;
        if (empty($recipientEmail)) {
            throw new \Exception(__('error.sale.recipient_email_required'));
        }

        // TODO: 실제 이메일 발송 로직 구현
        // Mail::to($recipientEmail)->send(new SaleStatementMail($sale, $data['message'] ?? null));

        return [
            'sent_to' => $recipientEmail,
            'sent_at' => now()->toIso8601String(),
            'statement_number' => $this->generateStatementNumber($sale),
        ];
    }

    /**
     * 거래명세서 번호 생성
     */
    private function generateStatementNumber(Sale $sale): string
    {
        return 'ST'.$sale->sale_number;
    }

    /**
     * 판매자 정보 조회
     */
    private function getSellerInfo(int $tenantId): array
    {
        $tenant = \App\Models\Tenants\Tenant::find($tenantId);

        return [
            'name' => $tenant->name ?? '',
            'business_number' => $tenant->business_number ?? '',
            'representative' => $tenant->representative ?? '',
            'address' => $tenant->address ?? '',
            'tel' => $tenant->tel ?? '',
            'fax' => $tenant->fax ?? '',
            'email' => $tenant->email ?? '',
        ];
    }

    /**
     * 구매자 정보 조회
     */
    private function getBuyerInfo($client): array
    {
        if (! $client) {
            return [];
        }

        return [
            'name' => $client->name ?? '',
            'business_number' => $client->business_number ?? '',
            'representative' => $client->representative ?? '',
            'address' => $client->address ?? '',
            'tel' => $client->tel ?? '',
            'fax' => $client->fax ?? '',
            'email' => $client->email ?? '',
        ];
    }

    /**
     * 매출 품목 조회 (향후 확장)
     */
    private function getSaleItems(Sale $sale): array
    {
        // TODO: 매출 품목 테이블이 있다면 조회
        // 현재는 기본 매출 정보만 반환
        return [
            [
                'description' => $sale->description ?? '매출',
                'quantity' => 1,
                'unit_price' => $sale->supply_amount,
                'supply_amount' => $sale->supply_amount,
                'tax_amount' => $sale->tax_amount,
                'total_amount' => $sale->total_amount,
            ],
        ];
    }
}
