<?php

namespace App\Services\Customer;

use App\Entities\Customer\ServiceEntity;
use App\Entities\Equipment\ServerEntity;
use App\Entities\PaymentEntity;
use App\Helpers\Customer\ServiceHelper;
use App\Models\Customer\ServiceModel;
use App\Services\Equipment\ServerPartService;
use App\Services\Equipment\ServerService;
use App\Services\PaymentService;
use DateTime;

class ServiceService extends CustomerService
{
    private ?ServerService $_serverService = null;
    private ?PaymentService $_paymentService = null;
    private ?ServerPartService $_serverPartService = null;
    public function __construct()
    {
        parent::__construct(new ServiceModel(), new ServiceHelper());
        $this->addClassName('Service');
    }
    public function getFormFields(): array
    {
        return [
            "site",
            "location",
            "clientinfo_uid",
            'serverinfo_uid',
            "rack",
            "line",
            "start_at",
            "billing_at",
            "status",
            'sale',
            'amount',
            "history",
        ];
    }
    public function getFormFilters(): array
    {
        return [
            'site',
            'location',
            'clientinfo_uid',
            'serverinfo_uid',
            'rack',
            'line',
            'status',
        ];
    }
    public function getIndexFields(): array
    {
        return [
            'site',
            'location',
            'clientinfo_uid',
            'serverinfo_uid',
            'sale',
            'amount',
            'billing_at',
            'status',
            'start_at',
            'updated_at',
        ];
    }
    public function getIndexFilters(): array
    {
        return [
            'site',
            'location',
            'clientinfo_uid',
            'serverinfo_uid',
            'user_uid', //home의 최신신규서버현황에서 사용
            'status',
        ];
    }
    public function getBatchjobFields(): array
    {
        return ['site', 'location', 'clientinfo_uid', 'status'];
    }
    final public function getBatchjobButtons(): array
    {
        return [
            'batchjob' => '일괄 처리 ',
        ];
    }
    public function getFormRule(string $action, string $field): string
    {
        if (is_array($field)) {
            throw new \Exception(__FUNCTION__ . "=> field가 array 입니다.\n" . var_export($field, true));
        }
        switch ($field) {
            case "serverinfo_uid":
                $rule  = "required|numeric";
                break;;
            default:
                $rule = parent::getFormRule($action, $field);
                break;
        }
        return $rule;
    }
    protected function getEntity_process(mixed $entity): ServiceEntity
    {
        if (!$entity instanceof ServiceEntity) {
            throw new \Exception(__METHOD__ . "에서 형식오류:ServiceEntity만 허용됩니다.");
        }
        //서버정보 정의
        $serverEntity = $this->getServerService()->getEntity($entity->getServerInfoUID());
        if ($serverEntity instanceof ServerEntity) {
            $entity->setServerEntity($serverEntity);
        }
        //결제정보 정의
        if ($entity->getPaymentUID()) {
            $paymentEntity = $this->getPaymentService()->getEntity($entity->getPaymentUID());
            if ($paymentEntity instanceof PaymentEntity) {
                $entity->setPaymentEntity($paymentEntity);
            }
        }
        return $entity;
    }
    final public function getPaymentService(): PaymentService
    {
        if (!$this->_paymentService) {
            $this->_paymentService = new PaymentService();
        }
        return $this->_paymentService;
    }
    public function getServerService(): ServerService
    {
        if (!$this->_serverService) {
            $this->_serverService = new ServerService();
        }
        return $this->_serverService;
    }
    final public function getServerPartService(): ServerPartService
    {
        if (!$this->_serverPartService) {
            $this->_serverPartService = new ServerPartService();
        }
        return $this->_serverPartService;
    }
    //interval을 기준으로 최근 신규 서비스정보 가져오기
    final public function getEntitiesByNewService(int $interval, string $status = ServiceEntity::DEFAULT_STATUS): array
    {
        return $this->getEntities(sprintf("start_at >= NOW()-INTERVAL {$interval} DAY AND status = '%s'", $status));
    }
    //서비스별 총 금액
    final public function getTotalAmounts($where = []): array
    {
        $rows = $this->getModel()->groupBy('clientinfo_uid')->select("clientinfo_uid,SUM(amount) AS amount")
            ->where($where)
            ->get()->getResult();
        $amounts = [];
        foreach ($rows as $row) {
            $amounts[$row->clientinfo_uid] = $row->amount;
        }
        return $amounts;
    }
    //다음 달로 결제일 가져오기.
    final public function getNextMonthDate(ServiceEntity $entity): string
    {
        // $sql = "UPDATE serviceinfo SET billing_at =
        //         IF(DAY(billing_at) > DAY(LAST_DAY(billing_at)),
        //             LAST_DAY(DATE_ADD(billing_at, INTERVAL 1 MONTH)),
        //             DATE_ADD(billing_at, INTERVAL 1 MONTH)
        //         ) WHERE uid = ?";
        // return $this->getModel()->query($sql, [$entity->getPK()]);
        // 입력된 날짜를 DateTime 객체로 변환
        $date = new DateTime($entity->getBillingAt());
        // 현재 일(day)을 저장
        $day = (int)$date->format('d');
        // 다음달로 이동 (DateInterval 사용)
        $date->modify('first day of next month');
        // 다음달의 마지막 날 계산
        $lastDayOfNextMonth = (int)$date->format('t');
        // 현재 날짜가 다음달의 마지막 날보다 크면 -> 마지막 날로 설정
        if ($day > $lastDayOfNextMonth) {
            $day = $lastDayOfNextMonth;
        }
        // 일(day)을 설정
        $date->setDate(
            (int)$date->format('Y'),
            (int)$date->format('m'),
            $day
        );
        // 최종 결과 리턴 (YYYY-MM-DD)
        return $date->format('Y-m-d');
    }
    //조정된 금액 설정
    final public function getCaculatedAmount(ServiceEntity $entity): int
    {
        //총서비스금액 계산
        //기본:상면비+회선비+서버금액(price)+서버파트연결(월비용)-할인액
        $caculatedAmount = $entity->getRack() + $entity->getLine() + $entity->getServerEntity()->getPrice();
        // echo "기본금액: {$entity->getRack()} + {$entity->getLine()} + {$entity->getServerEntity()->getPrice()} = {$caculatedAmount}\n";
        //해당 서비스(서버) 관련 결제방식(Billing)이 Month인 ServerPart 전체를 다시 검사하여 월청구액을 합산한다.
        foreach ($this->getServerPartService()->getEntities(['serverinfo_uid' => $entity->getServerEntity()->getPK()]) as $serverPartEntity) {
            if ($serverPartEntity->getBilling() === PAYMENT['BILLING']['MONTH']) { //월비용일때만 적용
                $caculatedAmount += $serverPartEntity->getTotalAmount(); //단가*Cnt
            }
        }
        // echo "총서비스금액: {$caculatedAmount} - 할인액: {$entity->getSale()} = 최종금액: " . ($caculatedAmount - $entity->getSale()) . "\n";
        // exit;
        return $caculatedAmount - $entity->getSale();
    }
    //서비스 총금액설정
    final public function setAmount(ServiceEntity $entity): ServiceEntity
    {
        $entity = parent::modify($entity, ['amount' => $this->getCaculatedAmount($entity)]);
        $entity = $this->getPaymentService()->setService('modify', $entity, []);
        return $entity;
    }
    //기본 기능부분
    //FieldForm관련용
    public function getFormOption(string $field, array $options = []): array
    {
        switch ($field) {
            case 'serverinfo_uid':
                $options = $this->getServerService()->getEntities();
                break;
            default:
                $options = parent::getFormOption($field, $options);
                break;
        }
        return $options;
    }
    //생성
    public function create(array $formDatas): ServiceEntity
    {
        //신규등록(월청구액 전달값 그대로 사용하므로 getCaculatedAmount 필요없음)
        $entity = parent::create($formDatas);
        if (!array_key_exists('serverinfo_uid', $formDatas)) {
            throw new \Exception(__METHOD__ . "에서 오류발생: 서버가 지정되지 않았습니다.");
        }
        //서버등록
        $entity = $this->getServerService()->setService('create', $entity, $formDatas);
        //전체 서비스금액 설정
        $entity = $this->getPaymentService()->setService('create', $entity, $formDatas);
        $entity = parent::modify($entity, ['paymentifo_uid' => $entity->getPaymentEntity()->getPK()]);
        return $entity;
    }
    //수정
    public function modify(mixed $entity, array $formDatas): ServiceEntity
    {
        //수정전 정보
        $oldEntity = clone $entity; //반드시 clone 할것
        //서비스정보 수정
        $entity = parent::modify($entity, $formDatas);
        //기존 서버정보와 다르다면 서버변경
        if ($oldEntity->getServerEntity()->getPK() != $formDatas['serverinfo_uid']) {
            //기존서버처리
            $oldEntity = $this->getServerService()->setService('delete', $oldEntity, ['serverinfo_uid' => $oldEntity->getServerInfoUID()]);
            //수정할신규서버처리
            $entity = $this->getServerService()->setService('create', $entity, $formDatas);
        }
        //전체 서비스 금액 재계산 후 설정
        return $this->setAmount($entity);
    }
    //삭제
    public function delete(mixed $entity): ServiceEntity
    {
        //서비스서버해지
        foreach ($this->getServerService()->getEntities(['serviceinfo_uid' => $entity->getPK()]) as $serverEntity) {
            $this->getServerService()->setService('delete', $entity, ['serverinfo_uid' => $serverEntity->getPK()]);
        }
        return parent::delete($entity);
    }
    //비고(History)설정
    public function history(mixed $entity, array $formDatas): ServiceEntity
    {
        return parent::modify($entity, $formDatas);
    }
    //대체서버추가(가격변동은 없음)
    public function addServer(ServiceEntity $entity, array $formDatas): ServiceEntity
    {
        if (!array_key_exists('serverinfo_uid', $formDatas)) {
            throw new \Exception(__METHOD__ . "에서 오류발생:대체서버가 지정되지 않았습니다.");
        }
        $formDatas['type'] = 'alternative'; //대체서버로 등록하기위해 필요
        return $this->getServerService()->setService('create', $entity, $formDatas);
    }
    //대체서버를 메인서버로 설정
    public function changeServer(ServiceEntity $entity, array $formDatas): ServiceEntity
    {
        //수정전 정보
        $oldEntity = clone $entity; //반드시 clone 할것
        //기존 서버의 Type정보가져오기 위함
        $oldServerEntity = $oldEntity->getServerEntity();
        if (!$oldServerEntity instanceof ServerEntity) {
            throw new \Exception(__METHOD__ . "에서 오류발생: 기존 메인 서버정보를 찾을수 없습니다.");
        }
        $type = $oldServerEntity->getType();
        //기존메인서버 정보를 대체서버로 변경
        $oldEntity = $this->getServerService()->setService('modify', $oldEntity, ['type' => SERVER['TYPES']['ALTERNATIVE']]);
        //서비스정보 수정
        $entity = parent::modify($entity, $formDatas);
        //메인서버로 선정된 대체서버정보 Type을 기존 메인서버의 Type으로 변경
        $entity = $this->getServerService()->setService('modify', $entity, ['type' => $type]);
        //전체 서비스 금액 재계산 후 설정
        return $this->setAmount($entity);
    }
    //대체서버해지(대체서버는 해지는 가격변동은 없음)
    public function terminateServer(ServiceEntity $entity, array $formDatas): ServiceEntity
    {
        if ($entity->getServerEntity()->getPK() === $formDatas['serverinfo_uid']) {
            throw new \Exception(__METHOD__ . "에서 오류발생: 서비스의 메인 서버정보는 해지할 수 없습니다.");
        }
        //대체서버해지 및 결제처리는 하지않음
        return $this->getServerService()->setService('delete', $entity, ['serverinfo_uid' => $formDatas['serverinfo_uid']]);
    }
}
