<?php

namespace App\Controller;

use App\Bundle\Infoclinica\Region;
use App\Entity\Record;
use App\Entity\PriceDepartment;
use App\Entity\PriceList;
use App\Entity\Filial;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\HttpFoundation\Request;
use App\Bundle\Utils\Logger;
use Symfony\Contracts\HttpClient\HttpClientInterface;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\HttpClient\CachingHttpClient;
use Symfony\Component\HttpKernel\HttpCache\Store;
use OpenApi\Annotations as OA;
use App\Service\SpecialistService;
use App\Service\PriceListService;
use Knp\Component\Pager\PaginatorInterface;

/**
 * @OA\Info(title="Open API sovamed", 
 *  description="Справочник методов доступных в Open API sovamed.",
 *  version="3.0.0"
 * )
 * 
 * @OA\Server( url="https://dev.sovamed.ru/api", description="Open API sovamed")
 * 
 * @Route("/api")
 */
class PublicAPIController extends AbstractController
{
    private $client;

    public function __construct(HttpClientInterface $client, string $rootPath)
    {
        $store = new Store($rootPath . '/var/HttpClient');
        $this->client = new CachingHttpClient($client, $store);
    }

    /**
     * @Route("/anonymous-reserve", methods={"POST"})
     */
    public function anonymousReserve(Request $request): Response 
    {
        try {
            $timezone = Region::getTimezone();
            
            if (!empty($request->request->get('timezone'))) {
                $timezone = (int) $request->request->get('timezone');
            }

            $reserve = [
                'date' => date('Ymd', strtotime($request->request->get('workDate'))),
                'st' => explode('-', $request->request->get('time'))[0],
                'en' => explode('-', $request->request->get('time'))[1],
                'services' => [],
                'filial' => (int) $request->request->get('filial'),
                'timezone' => $timezone,
                'schedident' => (int) $request->request->get('schedident'),
                'rnum' => $request->request->get('rnum') === 'undefined' ? null : $request->request->get('rnum'),
                'dcode' => (int) $request->request->get('specialist')
            ];
            
            $requestData = [
                'accept' => 'true',
                'fio' => $request->request->get('fio'),
                'captcha' => $request->request->get('captcha'),
                'email' => $request->request->get('email'),
                'phone' => $request->request->get('phone'),
                'reserve' => json_encode($reserve, JSON_UNESCAPED_SLASHES)
            ];

            $referer = $request->headers->get('referer');
            $response = $this->client->request('POST', '/api/reservation/anonymous-reserve', [
                'verify_peer' => false,
                'verify_host' => false,
                'base_uri' => $_ENV['MIS'],
                'headers' => [
                    'Referer' => $referer,
                    'User-Agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:66.0) Gecko/20100101 Firefox/66.0',
                    'Accept-Language' => 'ru-RU,ru;q=0.8,en-US;q=0.5,en;q=0.3',
                    'Accept' => 'application/json, text/javascript, */*; q=0.01',
                    'Content-Type' => 'application/json; charset=UTF-8',
                    'X-Requested-With' => 'XMLHttpRequest',
                    'X-Integration-Type' => 'WEBSDK'
                ],
                'body' => json_encode($requestData)
            ]);

            // Проверяем статус ответа
            $statusCode = $response->getStatusCode();
            
            if ($statusCode !== 200) {
                throw new \Exception("External API returned status: {$statusCode}");
            }

            $intervals = $response->toArray();

            // Сохраняем запись
            $entityManager = $this->getDoctrine()->getManager();
            $record = new Record();
            $record
                ->setSpecialistId((int) $request->request->get('specialist'))
                ->setPhone($request->request->get('phone'))
                ->setHash($request->request->get('phone'))
                ->setReserve($reserve)
                ->setCreateAt(new \DateTime('NOW'));
            
            $entityManager->persist($record);
            $entityManager->flush();

            return $this->json([
                'success' => true,
                'data' => [
                    'intervals' => $intervals,
                    'hash' => md5($request->request->get('phone')),
                    'phone' => $request->request->get('phone'),
                    'recordId' => $record->getId(),
                ]
            ]);

        } catch (\Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface $e) {
            // Ошибка 4xx
            return $this->json([
                'success' => false,
                'error' => 'Client error: ' . $e->getMessage()
            ], 400);
        } catch (\Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface $e) {
            // Ошибка 5xx
            return $this->json([
                'success' => false,
                'error' => 'Server error: ' . $e->getMessage()
            ], 502);
        } catch (\Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface $e) {
            // Ошибки сети
            return $this->json([
                'success' => false,
                'error' => 'Network error: ' . $e->getMessage()
            ], 503);
        } catch (\Exception $e) {
            // Другие ошибки
            return $this->json([
                'success' => false,
                'error' => 'Internal error: ' . $e->getMessage()
            ], 500);
        }
    }

    /**
     * @OA\Get(
     *   tags= {"Расписание врача"},
     *   path="/interval",
     *   summary="Получение сетки расписания",
     *   @OA\Parameter(
     *      name="startInterval",
     *      description="Начальная дата (Y-m-d)",
     *      in="query",
     *      required=true,
     *        @OA\Schema(
     *          type="string",
     *          format="Y-m-d"
     *         
     *        )
     *   ),
     *   @OA\Parameter(
     *      name="endInterval",
     *      description="Конечна дата (Y-m-d)",
     *      in="query",
     *      required=true,
     *        @OA\Schema(
     *          type="string",
     *          format="Y-m-d"
     *         
     *        )
     *   ),
     *   @OA\Parameter(
     *      name="department",
     *      description="ID отделения",
     *      in="query",
     *      required=true,
     *        @OA\Schema(
     *          type="string"
     *        )
     *   ),
     *   @OA\Parameter(
     *      name="doctor",
     *      description="ID врача",
     *      in="query",
     *      required=true,
     *        @OA\Schema(
     *          type="string"
     *        )
     *   ),
     *   @OA\Parameter(
     *      name="filial",
     *      description="ID филиала",
     *      in="query",
     *      required=true,
     *      @OA\Schema(
     *           type="string"
     *      )
     *   ),
     *   @OA\Response(
     *     response=200,
     *     description="json response"
     *   )
     * )
     *  
     * @Route("/interval", methods={"GET"})
     */
    public function interval(Request $request): Response 
    {
        $dateFormat = $request->query->get('dateFormat');
        
        if (empty($dateFormat)) {
            $dateFormat = 'Y-m-d';
        }

        $startInterval = $request->query->get('startInterval');
        $endInterval = $request->query->get('endInterval');
        $doctor = $request->query->get('doctor');
        $department = $request->query->get('department');
        $filial = $request->query->get('filial');
        $onlineMode = $request->query->get('onlineMode')? true: false;
        $isFree = true;
        $nearestDate = NULL;

        if (empty($doctor) || empty($startInterval) || empty($endInterval) || empty($department) || empty($filial)) {
            throw new BadRequestHttpException('Bad request');
        }

        $schedules = $this->getSchedule($doctor, $department, $filial, $onlineMode, $startInterval, $endInterval); 
        $intervals = $this->getInterval($doctor, $department, $filial, $onlineMode, $startInterval, $endInterval);

        $findInterval = function ($schedident, $workDate) use($intervals, $onlineMode, $isFree, $nearestDate, $dateFormat) {
            $intervalsData = [];

            if (!empty($intervals)) {
                foreach ($intervals[date('Ymd', strtotime($workDate))] as $key => $interval) {
                    if ($interval['schedident'] == $schedident) {
                        $intervalsData[$key]['time'] = $interval['time'];
                        $intervalsData[$key]['rNum'] = isset($interval['rNum'])? $interval['rNum']: null;
                        $intervalsData[$key]['startTime'] = explode('-', $interval['time'])[0];
                        $intervalsData[$key]['endTime'] = explode('-', $interval['time'])[1];
                        $intervalsData[$key]['schedident'] = $interval['schedident'];
                        $intervalsData[$key]['isFree'] = $interval['isFree'];
                        $intervalsData[$key]['onlineMode'] = $onlineMode;
                        $intervalsData[$key]['workDate'] = $interval['workDate']->format($dateFormat);

                        if ($interval['isFree'] && $isFree && is_null($nearestDate)) {
                            $nearestDate = $interval['workDate']->format('Y-m-d');
                            $intervalsData[$key]['nearestDate'] = $interval['workDate']->format($dateFormat);
                            $isFree = false;
                        }
                    }
                }
            }

            return $intervalsData;
        };

        $dataResponse = [];
        $i = 0;

        if (isset($schedules['success'])) {
            if ($schedules['success'] == true) {
                $uniqueIntervals = [];

                foreach ($schedules['data'] as $key => $data) {
                    uasort($data['intervals'], function ($a, $b) {
                        if ($a['workDate'] == $b['workDate']) {
                            return $a['startInterval'] <=> $b['startInterval'];
                        }
                        return 0;
                    });

                    foreach($data['intervals'] as $interval) {
                        if ($interval['isFree'] === true) {
                            $workDate = date($dateFormat, strtotime($interval['workDate']));

                            $uniqueKey = $interval['schedident']
                                . '-' . $workDate
                                . '-' . $interval['startInterval']
                                . '-' . $interval['endInterval'];

                            if (!isset($uniqueIntervals[$uniqueKey])) {
                                $dataIntervals = $findInterval($interval['schedident'], $workDate);
                                if ($dataIntervals) {
                                    $uniqueIntervals[$uniqueKey] = [
                                        'workDate' => $workDate,
                                        'isFree' => $interval['isFree'],
                                        'startInterval' => $interval['startInterval'],
                                        'endInterval' => $interval['endInterval'],
                                        'intervals' => $dataIntervals
                                    ];
                                }
                            }
                        }
                    }
                }

                $dataResponse = array_values($uniqueIntervals);
            }
        }


        $uid = false;

        if (! is_null($this->getUser())) {
            $uid = $this->getUser()->getUid();
        }

        return $this->json(['data' => ['userInfo' => $uid, 'intervalsData' => $dataResponse]]);
    }

    private function getInterval($doctor, $department, $filial, $onlineMode, $startInterval, $endInterval)
    {
        $response = $this->client->request('GET', '/api/reservation/intervals', [
            'verify_peer' => false,
            'verify_host' => false,
            'base_uri' => $_ENV['MIS'],
            'headers' => [
                'Content-Type' => 'application/json',
                'User-Agent' => 'sovamed_bot'
            ],
            'query' => [
                'dcode' => $doctor,
                'spec' => $department,
                'onlineMode' => ($onlineMode ? 1 : 0),
                'st' => \date("Ymd", strtotime($startInterval)),
                'en' => \date("Ymd", strtotime($endInterval)),
                'filialId' => $filial,
                'inFilials' => $filial
            ]
        ]);

        $intervals = $response->toArray();
        $dataResponse = [];

        if (isset($intervals['data'])) {
            foreach ($intervals['data'] as $data) {
                if (isset($data['workdates'])) {
                    foreach ($data['workdates'] as $key => $workdates) {
                        foreach ($workdates as $workdate => $item) {
                            $workDate = \DateTime::createFromFormat(
                                'Ymd', 
                                $workdate
                            );

                            $intervalKey = 0;

                            for ($i=0; $i < count($item); $i++) { 
                                foreach ($item[$i]['intervals'] as $intervaldata) {
                                    $dataResponse[$workdate][$intervalKey]['workDate'] = $workDate;
                                    $dataResponse[$workdate][$intervalKey]['schedident'] = $item[$i]['schedident'];
                                    $dataResponse[$workdate][$intervalKey]['time'] = $intervaldata['time'];
                                    $dataResponse[$workdate][$intervalKey]['isFree'] = $intervaldata['isFree'];
                                    $dataResponse[$workdate][$intervalKey]['rNum'] = isset($item[$i]['rnum'])? $item[$i]['rnum']: null;
                                    $intervalKey++;
                                }
                            }
                        }
                    }
                }
            }
        }
        
        return $dataResponse;
    }

    private function getSchedule($doctor, $department, $filial, $onlineMode, $startInterval, $endInterval)
    {
        $response = $this->client->request('GET', '/api/reservation/schedule', [
            'verify_peer' => false,
            'verify_host' => false,
            'base_uri' => $_ENV['MIS'],
            'headers' => [
                'Content-Type' => 'application/json',
                'User-Agent' => 'sovamed_bot'
            ],
            'query' => [
                'doctor' => $doctor,
                'department' => $department,
                'onlineMode' => ($onlineMode ? 1 : 0),
                'st' => date("Ymd", strtotime($startInterval)),
                'en' => date("Ymd", strtotime($endInterval)),
                'filialId' => $filial
            ]
        ]);
        
        return $response->toArray();
    }

    /**
     * @Route("/userInfo", methods={"GET"})
     */
    public function user(): Response 
    {
        $uid = false;

        if (! is_null($this->getUser())) {
            $uid = $this->getUser()->getUid();
        }
        
        return $this->json(['data' => $uid]);
    }

    /**
     * @OA\Get(
     *   tags= {"Услуги и цены"},
     *   path="/pricelist/departments",
     *   summary="Получение списка отделений",
     *  @OA\Response(
     *     response=200,
     *     description="json response"
     *  )
     * )
     * 
     * @Route("/pricelist/departments", methods={"GET"})
     */
    public function pricelistDepartments(Request $request): Response 
    {
        $response = [];

        $entityManager = $this->getDoctrine()->getManager();
        $departments = $entityManager->getRepository(PriceDepartment::class)
            ->findAll();

        if ($departments) {
            foreach ($departments as $key => $item) {
                $item = $item->toArray();
                
                unset($item['__initializer__']);
                unset($item['__isInitialized__']);
                unset($item['__cloner__']);
                unset($item['id']);
                $response[$key] = $item;
            }
        }

        return $this->json(['data' => $response]);
    }

    /**
     * @OA\Get(
     *   tags= {"Услуги и цены"},
     *   path="/pricelist",
     *   summary="Получение списка услуг и цен",
     *   @OA\Parameter(
     *      name="depnum",
     *      description="ID отделения",
     *      in="query",
     *      required=true,
     *      @OA\Schema(
     *           type="string"
     *      )
     *   ),
     *   @OA\Parameter(
     *      name="filial",
     *      description="ID филиала",
     *      in="query",
     *      required=false,
     *      @OA\Schema(
     *           type="string"
     *      )
     *   ),
     *   @OA\Parameter(
     *      name="active",
     *      description="Только активные",
     *      in="query",
     *      required=false,
     *      @OA\Schema(
     *           type="boolean"
     *      )
     *   ),
     * @OA\Response(
     *     response=200,
     *     description="json response"
     * )
     * )
     * 
     * @Route("/pricelist", methods={"GET"})
     */
    public function pricelist(Request $request, PaginatorInterface $paginator, PriceListService $priceListService): JsonResponse 
    {
        $params = [
            'kodoper' => $request->query->get('kodoper'),
            'groupId' => $request->query->get('depnum'),
            'filial' => $request->query->get('filial'),
            'actual' => $request->query->get('active')
        ];

        $priceListQuery = $priceListService->getPriceListQuery($params);

        $pagination = $paginator->paginate(
            $priceListQuery->getQuery(),
            $request->query->getInt('page', 1),
            1000
        );

        $totalItems = $pagination->getTotalItemCount();    // Общее количество элементов
        $itemCount = $pagination->count();                 // Количество элементов на текущей странице
        $currentPage = $pagination->getCurrentPageNumber(); // Текущая страница
        $totalPages = ceil($totalItems / 1000);              // Общее количество страниц
        
        return $this->json([
            'items' => $pagination,
            'totalItems' => $totalItems,
            'totalPages' => $totalPages,
            'currentPage' => $currentPage,
            'itemCount' => $itemCount
        ]);
    }

    private function getSpecialistResponse($specialist)
    {
        $response = [];

        if ($specialist) {
            $response = $specialist->toArray();
            
            unset($response['pecialistMore']);
            
            $response['img'] = 'https://api.sovamed.ru/specialist/picture/' . $specialist->getId();

            if (!empty($response['kinder'])) {
                $response['kinder'] = $response['kinder'] . ' ' . $this->textYear($response['kinder'], false);
            }

            if (!empty($response['experience'])) {
                $response['experience'] = $response['experience'] . ' ' . $this->textYear($response['experience'], true);
            }

            $specialistMore = $specialist->getSpecialistMore();
            
            if ($defaultLocation = $specialistMore->defaultLocation()) {
                $response['nearestDate'] = $defaultLocation['nearestDate']; 
                $response['filial'] = [
                    'id' => $defaultLocation['filial'],
                    'address' => $defaultLocation['address'],
                ];
                $response['department'] = [
                    'id' => $defaultLocation['department'],
                    'name' => $defaultLocation['name'],
                ];
            }
            
            $response['reviews'] = $specialistMore->getReviews();
            $response['prices'] = $specialistMore->getPrices();
        }

        return $response;
    }

    private function textYear($year, $exp = true)
    {
        $t1 = 0; 
        $t2 = 0;
        $year = abs($year);
        $t1 = $year % 10;
        $t2 = $year % 100;
        if ($exp) {
             return ($t1 == 1 && $t2 != 11 ? "год" : ($t1 >= 2 && $t1 <= 4 && ($t2 < 10 || $t2 >= 20) ? "года" : "лет"));
        } else {
            return ($t1 == 1 ? "года" : "лет");
        }
    }

    /** 
     * @OA\Get(
     *   tags= {"Врачи"},
     *   path="/doctor",
     *   summary="Получение данных о враче",
     *   @OA\Parameter(
     *      name="sid",
     *      description="ID врача",
     *      in="query",
     *      required=true,
     *      @OA\Schema(
     *           type="string"
     *      )
     *   ),
     *   @OA\Parameter(
     *      name="reviews",
     *      description="Показывать отзывы",
     *      in="query",
     *      required=false,
     *      @OA\Schema(
     *           type="boolean",
     *           default=false
     *      )
     *   ),  
     *   @OA\Response(
     *     response=200,
     *     description="json response"
     *   )
     * )
     *
     * @Route("/doctor", methods={"GET"})
     */
    public function doctor(SpecialistService $specialistService, Request $request): Response 
    {
        if (empty($request->query->getInt('sid'))) {
            return $this->json(['data' => false]);
        }

        $specialist = $specialistService->show([
            'dcode' => $request->query->get('sid')
        ]);
        
        return $this->json(['data' => $this->getSpecialistResponse($specialist)]);
    }

    /** 
     * @OA\Get(
     * tags= {"Врачи"},  
     * path="/doctors/{region}",
     *   summary="Получение данных врачей по регионам",
     *   @OA\Parameter(
     *      name="region",
     *      description="Название города",
     *      in="path",
     *      required=true,
     *      @OA\Schema(
     *           type="string",
     *           default="saratov"
     *      )
     *   ),
     *   @OA\Parameter(
     *      name="reviews",
     *      description="Показывать отзывы",
     *      in="query",
     *      required=false,
     *      @OA\Schema(
     *           type="boolean",
     *           default=true
     *      )
     *   ),        
     *   @OA\Response(
     *     response=200,
     *     description="json response"
     *   )
     * )
     * 
     * @Route("/doctors/{region}", methods={"GET"})
     */
    public function index(SpecialistService $specialistService, Request $request, $region = 'saratov'): Response
    {
        $regionId = match($region) {
            'krasnodar' => 94,
            'voronej'   => 93,
            'volgograd' => 92,
            default     => 91
        };
        
        $pagination = $specialistService->listPaginated(
            ['regionId' => $regionId], 
            $request->query->getInt('page', 1),
            500
        );

        $totalItems = $pagination->getTotalItemCount();    // Общее количество элементов
        $itemCount = $pagination->count();                 // Количество элементов на текущей странице
        $currentPage = $pagination->getCurrentPageNumber(); // Текущая страница
        $totalPages = ceil($totalItems / 1000);              // Общее количество страниц

        $response = [];

        foreach ($pagination as $key => $specialist) {
            $response[$key] = $this->getSpecialistResponse($specialist);
        }

        return $this->json([
            'data' => $response,
            'totalItems' => $totalItems,
            'totalPages' => $totalPages,
            'currentPage' => $currentPage,
            'itemCount' => $itemCount
        ]);
    }

}
