<?php

namespace App\Services;

use App\Enums\BookingStatus;
use App\Repositories\BookingNoteRepository;
use App\Repositories\BookingRepository;
use App\Repositories\CampaignRepository;
use App\Repositories\CreatorRepository;
use App\Repositories\VideoRepository;
use App\Support\IdFormatter;
use App\Support\Uuid;
use App\Utils\AuditLog;

class BookingService extends BaseService
{
    protected $repository;
    protected $videoRepository;
    protected $creatorRepository;
    protected $campaignRepository;
    protected $bookingNoteRepository;

    public function __construct(
        BookingRepository $repository,
        VideoRepository $videoRepository,
        CreatorRepository $creatorRepository,
        CampaignRepository $campaignRepository,
        BookingNoteRepository $bookingNoteRepository
    ) {
        $this->repository = $repository;
        $this->videoRepository = $videoRepository;
        $this->creatorRepository = $creatorRepository;
        $this->campaignRepository = $campaignRepository;
        $this->bookingNoteRepository = $bookingNoteRepository;
    }

    public function getListWithPaginate(?string $orgHex, int $perPage = 15)
    {
        return $this->repository->paginateByOrganization($orgHex, $perPage);
    }

    public function bulkCreateBookings(string $campaignId, array $kocIds, string $bookingType, $userId, ?string $orgHex): array
    {
        $success = 0;
        $failed = 0;
        $errors = [];
        $alreadyExists = [];

        foreach ($kocIds as $creatorId) {
            try {
                $existing = $this->getBookingsByCampaignAndCreator($campaignId, $creatorId, $orgHex, $bookingType);
                if ($existing) {
                    $alreadyExists[] = $creatorId;
                    $failed++;
                    continue;
                }
                $booking = $this->createBooking([
                    'campaign_id' => $campaignId,
                    'creator_id' => $creatorId,
                    'created_by_id' => $userId,
                    'type' => $bookingType,
                ], $orgHex);
                if ($booking) $success++;
                else $failed++;
            } catch (\Illuminate\Database\QueryException $e) {
                if (str_contains($e->getMessage(), 'bookings_campaign_creator_type_unique')) {
                    $alreadyExists[] = $creatorId;
                } else {
                    $errors[] = 'KOC ' . $creatorId . ': ' . $e->getMessage();
                }
                $failed++;
            } catch (\Exception $e) {
                $failed++;
                $errors[] = 'KOC ' . $creatorId . ': ' . $e->getMessage();
            }
        }

        return compact('success', 'failed', 'alreadyExists', 'errors');
    }

    public function createBooking(array $data, ?string $orgHex)
    {
        $data = $this->prepareCreateData($data, $orgHex, ['campaign_id', 'creator_id', 'assigned_to_id']);

        if (empty($data['assigned_to_id']) && !empty($data['created_by_id'])) {
            $data['assigned_to_id'] = $data['created_by_id'];
        }

        if (!isset($data['status'])) {
            $data['status'] = BookingStatus::default()->value;
        }

        $booking = $this->repository->create($data);

        $bookingType = $data['type'] ?? 'video';
        try {
            if ($bookingType === 'video') {
                $booking->bookingVideo()->create([
                    'air_date' => $data['air_date'] ?? null,
                    'number_of_videos' => $data['number_of_videos'] ?? 0,
                ]);
            } elseif ($bookingType === 'livestream') {
                $booking->bookingLivestream()->create([
                    'sample_type' => $data['sample_type'] ?? null,
                    'deal_live_registration' => $data['deal_live_registration'] ?? null,
                ]);
            }
        } catch (\Exception $e) {
        }

        $kocName = $this->resolveCreatorName($data['creator_id'] ?? null, $orgHex);
        $campaignName = $this->resolveCampaignName($data['campaign_id'] ?? null, $orgHex);
        $details = trim(($kocName ? 'KOC: ' . $kocName : '') . ($campaignName ? ' - Chiến dịch: ' . $campaignName : ''));
        AuditLog::log('add_koc_to_campaign', 'campaign_koc', IdFormatter::formatModelId($booking), $kocName, $details ?: null, AuditLog::detectTabFromReferer('booking'));

        return $booking;
    }

    public function updateBooking(string $id, array $data, ?string $orgHex)
    {
        if (!empty($data['assigned_to_id'])) {
            $bin = IdFormatter::hexToBinary($data['assigned_to_id']);
            if ($bin) {
                $data['assigned_to_id'] = $bin;
            }
        }

        $old = $this->repository->getByIdInOrganization($id, $orgHex);
        if (!$old) {
            return false;
        }

        if (array_key_exists('status', $data)) {
            $newStatus = $data['status'];
            $oldStatus = $old->status;

            if ($newStatus !== $oldStatus) {
                $data = $this->applyStatusTimestamps($data, $newStatus, $oldStatus, $old);
            }
        }

        $booking = $this->repository->updateInOrganization($id, $data, $orgHex);

        if ($booking) {
            $bookingType = $booking->type ?? 'video';
            $this->updateTypeSpecificData($booking, $data, $bookingType);

            $kocName = $this->resolveCreatorNameFromBooking($old, $orgHex);

            $fieldLabels = [
                'status' => 'Trạng thái', 'engagement_natural' => 'Engagement tự nhiên',
                'engagement_ads' => 'Engagement quảng cáo', 'cast_cost' => 'Chi phí cast',
                'sample_cost' => 'Chi phí mẫu', 'shipping_cost' => 'Chi phí ship',
                'price_quote' => 'Báo giá',
                'order_code' => 'Mã đơn hàng', 'send_date' => 'Ngày gửi',
                'eta_date' => 'Ngày nhận dự kiến',
                'tiktok_link' => 'Link TikTok',
                'assigned_to_id' => 'Người phụ trách',
                'air_date' => 'Ngày lên sóng', 'number_of_videos' => 'Số lượng video',
                'sample_type' => 'Phân loại mẫu', 'deal_live_registration' => 'Đăng ký deal live',
            ];
            $details = $this->buildAuditDiff($data, $old, $fieldLabels, function ($field, $old) use ($bookingType) {
                if (in_array($field, ['air_date', 'number_of_videos'])) {
                    return $bookingType === 'video' ? ($old->bookingVideo?->{$field} ?? null) : null;
                }
                if (in_array($field, ['sample_type', 'deal_live_registration'])) {
                    return $bookingType === 'livestream' ? ($old->bookingLivestream?->{$field} ?? '') : '';
                }
                return $old->{$field} ?? '';
            });
            if (trim($details)) {
                AuditLog::log('update_booking', 'campaign_koc', IdFormatter::formatModelId($old), $kocName, trim($details), AuditLog::detectTabFromReferer('booking'));
            }
        }

        return $booking;
    }

    public function updateBookingStatus(string $id, string $newStatus, ?\DateTimeInterface $timestamp = null, ?string $orgHex = null)
    {
        $data = ['status' => $newStatus];
        $statusEnum = BookingStatus::tryFrom($newStatus);
        $tsField = $statusEnum?->timestampField();
        if ($tsField && $timestamp) {
            $data[$tsField] = $timestamp;
        }

        $old = $this->repository->getByIdInOrganization($id, $orgHex);
        $booking = $this->repository->updateInOrganization($id, $data, $orgHex);

        if ($booking) {
            $kocName = $this->resolveCreatorNameFromBooking($old, $orgHex);
            AuditLog::log('update_booking', 'campaign_koc', IdFormatter::formatModelId($booking), $kocName, 'Trạng thái: ' . $newStatus, AuditLog::detectTabFromReferer('booking'));
        }

        return $booking;
    }

    public function deleteBooking(string $id, ?string $orgHex)
    {
        $booking = $this->repository->getByIdInOrganization($id, $orgHex);
        if (!$booking) {
            return false;
        }

        $bookingName = $booking->creator ? $booking->creator->creator_name : null;

        $this->videoRepository->deleteByBookingIdInOrganization($id, $orgHex);
        $deleted = $this->repository->deleteInOrganization($id, $orgHex);

        if ($deleted) {
            $campaignName = $booking->campaign ? $booking->campaign->campaign_name : null;
            $details = trim(($bookingName ? 'Đã loại KOC: ' . $bookingName : '') . ($campaignName ? ' khỏi chiến dịch: ' . $campaignName : ''));
            AuditLog::log('remove_koc_from_campaign', 'campaign_koc', IdFormatter::formatModelId($booking), $bookingName, $details ?: null, AuditLog::detectTabFromReferer('booking'));
        }

        return $deleted;
    }

    public function getBookingById(string $id, ?string $orgHex)
    {
        return $this->repository->getByIdInOrganization($id, $orgHex);
    }

    public function getBookingWithRelations(string $id, ?string $orgHex)
    {
        return $this->repository->getWithRelationsInOrganization($id, $orgHex);
    }

    public function getBookingsByCampaignAndCreator(string $campaignId, string $creatorId, ?string $orgHex, ?string $type = null)
    {
        return $this->repository->getByCampaignAndCreator($campaignId, $creatorId, $orgHex, $type);
    }

    public function getVideosByBooking(string $bookingId, ?string $orgHex)
    {
        return $this->videoRepository->getByBookingIdInOrganization($bookingId, $orgHex);
    }

    private function applyStatusTimestamps(array $data, string $newStatus, string $oldStatus, $old): array
    {
        $statusOrder = ['negotiating', 'confirmed', 'completed'];
        $statusTimestampMap = [
            'negotiating' => 'negotiating_at',
            'confirmed' => 'confirmed_at',
            'completed' => 'completed_at',
        ];

        if ($newStatus === 'not_contacted') {
            foreach ($statusTimestampMap as $col) {
                $data[$col] = null;
            }
            return $data;
        }

        $now = now();
        $newIndex = array_search($newStatus, $statusOrder, true);
        $oldIndex = array_search($oldStatus, $statusOrder, true);

        if ($newIndex !== false) {
            for ($i = 0; $i <= $newIndex; $i++) {
                $column = $statusTimestampMap[$statusOrder[$i]];
                if (empty($old->{$column})) {
                    $data[$column] = $now;
                }
            }
        }

        if ($oldIndex !== false && $newIndex !== false && $newIndex < $oldIndex) {
            for ($i = $newIndex + 1; $i < count($statusOrder); $i++) {
                $data[$statusTimestampMap[$statusOrder[$i]]] = null;
            }
        }

        return $data;
    }

    private function updateTypeSpecificData($booking, array $data, string $bookingType): void
    {
        try {
            if ($bookingType === 'video') {
                $videoData = array_intersect_key($data, array_flip(['air_date', 'number_of_videos']));
                if (!empty($videoData)) {
                    $booking->bookingVideo()->updateOrCreate([], $videoData);
                }
            } elseif ($bookingType === 'livestream') {
                $liveData = array_intersect_key($data, array_flip(['sample_type', 'deal_live_registration']));
                if (!empty($liveData)) {
                    $booking->bookingLivestream()->updateOrCreate([], $liveData);
                }
            }
        } catch (\Exception $e) {
        }
    }

    private function resolveCreatorName(?string $creatorId, ?string $orgHex): ?string
    {
        if (empty($creatorId)) return null;
        try {
            $creator = $this->creatorRepository->getByIdInOrganization($creatorId, $orgHex);
            return $creator ? $creator->creator_name : null;
        } catch (\Exception $e) {
            return null;
        }
    }

    private function resolveCampaignName(?string $campaignId, ?string $orgHex): ?string
    {
        if (empty($campaignId)) return null;
        try {
            $campaign = $this->campaignRepository->getByIdInOrganization($campaignId, $orgHex);
            return $campaign ? $campaign->campaign_name : null;
        } catch (\Exception $e) {
            return null;
        }
    }

    public function getBookingNoteById(int $noteId, ?string $orgHex)
    {
        return $this->bookingNoteRepository->findById($noteId, $orgHex);
    }

    public function updateBookingNote(int $noteId, array $data, ?string $orgHex)
    {
        return $this->bookingNoteRepository->update($noteId, $data, $orgHex);
    }

    public function deleteBookingNote(int $noteId, ?string $orgHex)
    {
        return $this->bookingNoteRepository->delete($noteId, $orgHex);
    }

    private function resolveCreatorNameFromBooking($booking, ?string $orgHex): ?string
    {
        if ($booking && $booking->creator) {
            return $booking->creator->creator_name;
        }
        if ($booking && !empty($booking->creator_id)) {
            return $this->resolveCreatorName($booking->creator_id, $orgHex);
        }
        return null;
    }
}
