<?php

namespace Plugin\ApgSimpleCalendar\Controller\Admin;

use Eccube\Controller\AbstractController;
use Eccube\Service\CsvImportService;
use Eccube\Util\CacheUtil;
use Eccube\Util\StringUtil;
use Plugin\ApgSimpleCalendar\Entity\Config;
use Plugin\ApgSimpleCalendar\Entity\Holiday;
use Plugin\ApgSimpleCalendar\Form\Type\Admin\ConfigType;
use Plugin\ApgSimpleCalendar\Form\Type\Admin\HolidayImportType;
use Plugin\ApgSimpleCalendar\Form\Type\Admin\HolidayType;
use Plugin\ApgSimpleCalendar\Repository\ConfigRepository;
use Plugin\ApgSimpleCalendar\Repository\HolidaysRepository;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\StreamedResponse;
use Symfony\Component\Routing\Annotation\Route;

class ConfigController extends AbstractController
{
    /** @var ConfigRepository */
    protected $configRepository;

    /** @var HolidaysRepository */
    protected $holidayRepository;

    protected $csvFileName;

    const CSV_HEADER = [
        '日付(YYYYMMDD)',
        '休日名称',
    ];

    /**
     * ConfigController constructor.
     *
     * @param ConfigRepository $configRepository
     */
    public function __construct(
        ConfigRepository $configRepository,
        HolidaysRepository $holidaysRepository
    )
    {
        $this->configRepository = $configRepository;
        $this->holidayRepository = $holidaysRepository;
    }

    /**
     * @Route("/%eccube_admin_route%/apg_simple_calendar/config", name="apg_simple_calendar_admin_config")
     * @Template("@ApgSimpleCalendar/admin/config.twig")
     */
    public function index(Request $request)
    {
        $Config = $this->configRepository->get();
        return [
            'Config' => $Config,
        ];
    }

    /**
     * @Route("/%eccube_admin_route%/apg_simple_calendar/config/regular_holidays", name="apg_simple_calendar_admin_config_regular_holidays")
     * @Template("@ApgSimpleCalendar/admin/regular_holidays.twig")
     */
    public function regularHolidays(Request $request)
    {
        $Config = $this->configRepository->get();
        $form = $this->createForm(ConfigType::class, $Config);
        $form->handleRequest($request);

        if ($form->isSubmitted() && $form->isValid()) {
            /** @var Config $Config */
            $Config = $form->getData();
            $this->configRepository->save($Config);
            $this->entityManager->flush($Config);

            $this->addSuccess('登録しました。', 'admin');

            return $this->redirectToRoute('apg_simple_calendar_admin_config_regular_holidays');
        }

        return [
            'form' => $form->createView(),
        ];
    }

    /**
     * @param Request $request
     * @param null $id
     * @return array|\Symfony\Component\HttpFoundation\RedirectResponse
     *
     * @Route("/%eccube_admin_route%/apg_simple_calendar/config/individual_holidays", name="apg_simple_calendar_admin_config_individual_holidays")
     * @Route("/%eccube_admin_route%/apg_simple_calendar/config/individual_holidays/{id}/edit", requirements={"id" = "\d+"}, name="apg_simple_calendar_admin_config_individual_holidays_edit")
     * @Template("@ApgSimpleCalendar/admin/individual_holidays.twig")
     */
    public function individualHolidays(Request $request, $id = null)
    {
        $Holidays = $this->holidayRepository->findBy([], ['target_date' => 'DESC']);

        if (empty($id)) {
            $TargetHoliday = new Holiday();
        } else {
            $TargetHoliday = $this->holidayRepository->get($id);
        }

        $form = $this->createForm(HolidayType::class, $TargetHoliday);
        $form->handleRequest($request);

        if ($form->isSubmitted() && $form->isValid()) {
            /** @var Holiday $TargetHoliday */
            $TargetHoliday = $form->getData();
            if (empty($id)) {
                $targetDate = $TargetHoliday->getTargetDate();
                /** @var Holiday $duplicationHoliday */
                $duplicationHoliday = $this->holidayRepository->findOneBy(['target_date' => $targetDate]);
                if (!empty($duplicationHoliday)) {
                    $duplicationHoliday->setName($TargetHoliday->getName());
                    $TargetHoliday = $duplicationHoliday;
                }
            }
            $this->holidayRepository->save($TargetHoliday);
            $this->entityManager->flush($TargetHoliday);
            $message = empty($id) ? "登録しました。" : "更新しました。";
            $this->addSuccess($message, 'admin');

            return $this->redirectToRoute('apg_simple_calendar_admin_config_individual_holidays');
        }

        return [
            'form' => $form->createView(),
            'Holidays' => $Holidays,
            'TargetHoliday' => $TargetHoliday,
        ];
    }

    /**
     * @param Request $request
     * @param null $id
     * @return array|\Symfony\Component\HttpFoundation\RedirectResponse
     *
     * @Route("/%eccube_admin_route%/apg_simple_calendar/config/import_holidays", name="apg_simple_calendar_admin_config_import_holidays")
     * @Template("@ApgSimpleCalendar/admin/import_holidays.twig")
     */
    public function import(Request $request)
    {
        $form = $this->createForm(HolidayImportType::class);
        $form->handleRequest($request);

        $errors = [];
        if ($form->isSubmitted() && $form->isValid()) {
            $formFile = $form['file']->getData();
            if (!empty($formFile)) {
                log_info('定休日CSV登録開始');
                /** @var CsvImportService $csvImportService */
                $csvImportService = $this->getImportData($formFile);
                $entities = [];
                // 簡易チェック
                foreach ($csvImportService as $line => $row) {
                    $values = array_values($row);
                    if (count($values) < 2) {
                        $errors[] =
                            [
                                'line' => $line,
                                'message' => '正しいフォーマットになっていません。[日付(YYYYMMDD), 名称]'
                            ];
                        continue;
                    }
                    $name = $values[1];

                    try {
                        $targetDate = new \DateTime($values[0]);
                    } catch (\Exception $e) {
                        $errors[] =
                            [
                                'line' => $line,
                                'message' => '日付のフォーマットが間違えています。[値:' . $values[0] . '](正しい形式:YYYYMMDD)'
                            ];
                        continue;
                    }
                    if (strlen($name) > 255) {
                        $errors[] =
                            [
                                'line' => $line,
                                'message' => '名称が長すぎます。100文字以内となるようにしてください'
                            ];
                        continue;
                    }
                    $entity = new Holiday();
                    $entity->setName($name);
                    $entity->setTargetDate($targetDate);
                    $entities[] = $entity;
                }
                if (empty($errors)) {
                    $this->entityManager->createQueryBuilder()
                        ->delete(Holiday::class)
                        ->getQuery()
                        ->execute();
                    foreach ($entities as $key => $entity) {
                        $this->entityManager->persist($entity);
                    }
                    $this->holidayRepository->cacheClear();
                    $this->entityManager->flush();
                    $this->addSuccess('一括アップロードが完了しました。', 'admin');
                    $this->removeUploadedFile();
                    return $this->redirectToRoute('apg_simple_calendar_admin_config_individual_holidays');
                } else {
                    $this->addError('指定されたCSVファイルのフォーマットに問題があったため、取り込みを中断しました。[エラー詳細]をご確認ください。', 'admin');
                    $this->removeUploadedFile();
                }
            }
        }
        return [
            'form' => $form->createView(),
            'errors' => $errors,
        ];
    }

    /**
     * アップロードされたCSVファイルの削除
     */
    protected function removeUploadedFile()
    {
        if (!empty($this->csvFileName)) {
            try {
                $fs = new Filesystem();
                $fs->remove($this->eccubeConfig['eccube_csv_temp_realdir'] . '/' . $this->csvFileName);
            } catch (\Exception $e) {
                // エラーが発生しても無視する
            }
        }
    }

    /**
     * アップロードされたCSVファイルの行ごとの処理
     *
     * @param UploadedFile $formFile
     *
     * @return CsvImportService|bool
     */
    protected function getImportData(UploadedFile $formFile)
    {
        // アップロードされたCSVファイルを一時ディレクトリに保存
        $this->csvFileName = 'upload_' . StringUtil::random() . '.' . $formFile->getClientOriginalExtension();
        $formFile->move($this->eccubeConfig['eccube_csv_temp_realdir'], $this->csvFileName);

        $file = file_get_contents($this->eccubeConfig['eccube_csv_temp_realdir'] . '/' . $this->csvFileName);

        if ('\\' === DIRECTORY_SEPARATOR && PHP_VERSION_ID >= 70000) {
            // Windows 環境の PHP7 の場合はファイルエンコーディングを CP932 に合わせる
            // see https://github.com/EC-CUBE/ec-cube/issues/1780
            setlocale(LC_ALL, ''); // 既定のロケールに設定
            if (mb_detect_encoding($file) === 'UTF-8') { // UTF-8 を検出したら SJIS-win に変換
                $file = mb_convert_encoding($file, 'SJIS-win', 'UTF-8');
            }
        } else {
            // アップロードされたファイルがUTF-8以外は文字コード変換を行う
            $encode = StringUtil::characterEncoding($file);
            if (!empty($encode) && $encode != 'UTF-8') {
                $file = mb_convert_encoding($file, 'UTF-8', $encode);
            }
        }

        $file = StringUtil::convertLineFeed($file);

        $tmp = tmpfile();
        fwrite($tmp, $file);
        rewind($tmp);
        $meta = stream_get_meta_data($tmp);
        $file = new \SplFileObject($meta['uri']);

        set_time_limit(0);

        // アップロードされたCSVファイルを行ごとに取得
        $data = new CsvImportService($file, $this->eccubeConfig['eccube_csv_import_delimiter'], $this->eccubeConfig['eccube_csv_import_enclosure']);

        return $data->setHeaderRowNumber(0) ? $data : false;
    }

    /**
     * @Route("/%eccube_admin_route%/apg_simple_calendar/config/individual_holidays/{id}/delete", requirements={"id" = "\d+"}, name="apg_simple_calendar_admin_config_individual_holidays_delete", methods={"DELETE"})
     */
    public function delete(Request $request, $id, CacheUtil $cacheUtil)
    {
        $this->isTokenValid();

        /** @var Holiday $TargetHoliday */
        $TargetHoliday = $this->holidayRepository->find($id);
        if (!$TargetHoliday) {
            $this->deleteMessage();
            return $this->redirectToRoute('apg_simple_calendar_admin_config_individual_holidays');
        }
        try {
            $this->holidayRepository->delete($TargetHoliday);
            $this->holidayRepository->cacheClear();
            $this->entityManager->flush();

            $this->addSuccess('admin.common.delete_complete', 'admin');
            $cacheUtil->clearDoctrineCache();

            return $this->redirectToRoute('apg_simple_calendar_admin_config_individual_holidays');

        } catch (\Exception $e) {
            $message = trans('admin.common.delete_error_foreign_key', ['%name%' => $TargetHoliday->getName()]);
            $this->addError($message, 'admin');
        }
    }

    /**
     * @Route("/%eccube_admin_route%/apg_simple_calendar/config/export_holiday", name="apg_simple_calendar_admin_config_export_holidays")
     * @param Request $request
     *
     * @return StreamedResponse
     */
    public function export(Request $request)
    {
        // タイムアウトを無効にする.
        set_time_limit(0);

        $response = new StreamedResponse();
        $response->setCallback(function () use ($request) {

            $fp = fopen('php://output', 'w');
            // ヘッダー書き込み
            $header = self::CSV_HEADER;
            mb_convert_variables('SJIS-win', 'UTF-8', $header);
            fputcsv($fp, $header);

            /** @var Holiday[] $Holidays */
            $Holidays = $this->holidayRepository->findBy([], ['target_date' => 'desc']);

            foreach ($Holidays as $Holiday) {
                fputcsv($fp, [
                    mb_convert_encoding($Holiday->getTargetDate()->format('Ymd'), 'SJIS-win', 'UTF-8'),
                    mb_convert_encoding($Holiday->getName(), 'SJIS-win', 'UTF-8'),
                ]);
            }

            fclose($fp);

        });

        $now = new \DateTime();
        $filename = 'holidays_' . $now->format('YmdHis') . '.csv';
        $response->headers->set('Content-Type', 'application/octet-stream');
        $response->headers->set('Content-Disposition', 'attachment; filename=' . $filename);
        $response->send();

        return $response;
    }
}
