<?php

namespace PWH_DCFH\App\Admin\Controllers;

use DateInterval;
use DateTime;
use DateTimeZone;
use Exception;

if (!defined('ABSPATH')) {
    exit;
}

/**
 * Class PWH_DCFH_Syn_ICS_URL
 * Handles parsing of ICS URLs from Google Calendar and Outlook Live.
 */
class PWH_DCFH_Syn_ICS_URL
{
    public $url;

    public $time_steps;

    public $time_format;

    public $domain;

    /**
     * PWH_DCFH_Syn_ICS_URL constructor.
     *
     * @param string $url
     * @param string $time_steps
     * @param string $time_format
     */
    public function __construct( $url,  $time_steps,  $time_format)
    {
        $this->url = $url;
        $this->time_steps = $time_steps;
        $this->time_format = $time_format;
        $this->domain = strtolower(wp_parse_url($url, PHP_URL_HOST));
    }

    /**
     * Fetches and parses ICS data from the provided URL.
     * @throws Exception
     * @return array
     */
    public function data()
    {
        $transient = md5($this->domain);
        if (PWH_DCFH_PLUGIN_DEV_MOD) {
            delete_transient($transient);
        }
        $cached_data = get_transient($transient);
        if (false === $cached_data) {
            $dates = $this->parse_ics();
            $data = [];
            if ($this->domain === 'calendar.google.com') {
                $data = $this->process_google_calendar($dates);
            } elseif ($this->domain === 'outlook.live.com') {
                $data = $this->process_outlook_calendar($dates);
            }
            set_transient($transient, $data, HOUR_IN_SECONDS); // 1-Hours Cache
            $cached_data = $data;
        }

        return $cached_data;

    }

    /**
     * Parses the ICS data from the remote URL.
     * @return array
     */
    private function parse_ics()
    {
        $ics_dates = [];
        $response = wp_remote_get($this->url, ['timeout' => 15, 'sslverify' => false]);
        if (!is_wp_error($response) && wp_remote_retrieve_response_code($response) === 200) {
            $response_body = wp_remote_retrieve_body($response);
            $data = explode("BEGIN:", $response_body);
            foreach ($data as $key => $value) {
                $metas[$key] = explode("\n", $value);
            }
            if (!empty($metas)) {
                foreach ($metas as $key => $value) {
                    foreach ($value as $sub_key => $sub_value) {
                        $ics_dates = $this->get_dates($key, $sub_key, $sub_value, $ics_dates);
                    }
                }
            }
        }

        return $ics_dates;
    }

    /**
     * Processes Google Calendar ICS data.
     *
     * @param array $dates
     *
     * @throws Exception
     * @return array
     */
    private function process_google_calendar( $dates)
    {
        $timezone = isset($dates[1]['X-WR-TIMEZONE']) ? trim($dates[1]['X-WR-TIMEZONE']) : null;
        unset($dates[1]);
        $dates = array_values($dates);
        $data = ['events' => [], 'ics_data' => []];
        foreach ($dates as $event) {
            if (!isset($event['DTSTART;VALUE=DATE']) && !isset($event['DTSTART'])) {
                continue;
            }
            $event_name = $event['SUMMARY'] ?? '-';
            $dtstart_key = isset($event['DTSTART;VALUE=DATE']) ? 'DTSTART;VALUE=DATE' : 'DTSTART';
            $dtend_key = isset($event['DTEND;VALUE=DATE']) ? 'DTEND;VALUE=DATE' : 'DTEND';
            $dtstart = new DateTime($event[$dtstart_key]);
            $dtstart->setTimezone(new DateTimeZone($timezone));
            $dtend = new DateTime($event[$dtend_key]);
            $dtend->setTimezone(new DateTimeZone($timezone));
            $data = $this->build_ics_data($dtstart, $dtend, $event_name, $data);
        }

        return $data;
    }

    /**
     * Processes Outlook Live Calendar ICS data.
     *
     * @param array $dates
     *
     * @throws Exception
     * @return array
     */
    private function process_outlook_calendar( $dates)
    {
        $timezones = array_map(static function ($event) {
            return $event['TZID'] ?? '';
        }, $dates);
        $timezone = array_values(array_filter($timezones, 'strlen'))[0] ?? '';
        $data = ['events' => [], 'ics_data' => []];
        foreach ($dates as $event) {
            $start_key = "DTSTART;TZID=$timezone";
            $end_key = "DTEND;TZID=$timezone";
            if (isset($event[$start_key], $event[$end_key])) {
                $event_name = $event['SUMMARY'] ?? '-';
                $dtstart = new DateTime($event[$start_key]);
                $dtend = new DateTime($event[$end_key]);
                $data = $this->build_ics_data($dtstart, $dtend, $event_name, $data);
            }
        }

        return $data;
    }

    /**
     * Extracts key-value pairs from ICS data.
     *
     * @param int $key
     * @param int $sub_key
     * @param string $sub_value
     * @param array $ics_dates
     *
     * @return array
     */
    private function get_dates( $key,  $sub_key,  $sub_value,  $ics_dates)
    {
        if ($key !== 0 && $sub_key === 0) {
            $ics_dates[$key]["BEGIN"] = $sub_value;
        } else {
            $sub_value_arr = explode(":", $sub_value, 2);
            if (isset($sub_value_arr[1])) {
                $ics_dates[$key][$sub_value_arr[0]] = $sub_value_arr[1];
            }
        }

        return $ics_dates;
    }

    /**
     * @param DateTime $dtstart
     * @param DateTime $dtend
     * @param $event_name
     * @param array $data
     *
     * @return array
     */
    private function build_ics_data( $dtstart,  $dtend, $event_name,  $data)
    {
        $start_datetime = $dtstart->format('Y-m-d h:ia');
        $start_date = $dtstart->format('Y-m-d');
        $start_time = $dtstart->format($this->time_format);
        $end_datetime = $dtend->format('Y-m-d h:ia');
        $end_date = $dtend->format('Y-m-d');
        $end_time = $dtend->format($this->time_format);
        // Calculate duration
        $interval = $dtstart->diff($dtend);
        $duration = $interval->format('%h hours %i minutes');
        // Collect mid dates
        $mid_dates = [];
        $current_date = clone $dtstart;
        $day_interval = new DateInterval('P1D');
        $current_date->add($day_interval);
        while ($current_date < $dtend) {
            $mid_dates[] = $current_date->format('Y-m-d');
            $current_date->add($day_interval);
        }
        // Collect date times
        $date_times = [];
        $current_time = clone $dtstart;
        $time_interval = new DateInterval("PT{$this->time_steps}M");
        while ($current_time < $dtend) {
            $date_times[] = $current_time->format($this->time_format);
            $current_time->add($time_interval);
        }
        $date_times[] = $dtend->format($this->time_format);
        $data['events'][] = [
            'event_name' => $event_name,
            'start_datetime' => $start_datetime,
            'end_datetime' => $end_datetime,
            'start_date' => $start_date,
            'end_date' => $end_date,
            'start_time' => $start_time,
            'end_time' => $end_time,
            'duration' => $duration,
            'mid_dates' => $mid_dates,
        ];
        if (!isset($data['ics_data'][$start_date])) {
            $data['ics_data'][$start_date] = implode(',', $date_times);
        } else {
            $data['ics_data'][$start_date] .= ','.implode(',', $date_times);
        }

        return $data;
    }
}
