<?php

class BusinessFacts extends Base {

    // Variables

    private $current_user_serial = 0;
    private $current_user_id = "";
    private $current_user_name = "";
    private $current_user_email = "";
    private $pagination_size = 20;
    private $pagination_page = 1;

    // =================
    // Class Constructor
    // =================

    public function __construct() {

        set_error_handler(array($this, "displayError"));

        date_default_timezone_set(self::TIMEZONE);

        $this->current_user_serial = $_SESSION[self::USER_SERIAL] ?? 0;
        $this->current_user_id = $_SESSION[self::USER_ID] ?? "";
        $this->current_user_name = $_SESSION[self::USER_NAME] ?? "";
        $this->current_user_email = $_SESSION[self::USER_EMAIL] ?? "";

        $this->pagination_page = $_SESSION[self::PAGINATION_PAGE] ?? 1;
    }

    // =====================
    // Search Business Facts
    // =====================

    public function searchBusinessFacts() {

        $subscription_serial = $_SESSION['subscription_serial'];
        $usersubscription_serial = $_SESSION['usersubscription_serial'];

        $subscription = (new Subscriptions())->getSubscription($subscription_serial);

        if ($subscription->count() === 0) {
            $this->logError("Invalid Subscription ({$subscription_serial}): " . __METHOD__);
        }

        $userssubscription = (new Users())->getUserSubscription($usersubscription_serial);

        $user_subscription_start_date = $userssubscription->record->usersubscription_start_date;
        $user_subscription_end_date = $userssubscription->record->usersubscription_end_date;

        $searchhistory = (new SearchHistory())->getUsersPreviousSearchParameters($this->current_user_serial, $subscription_serial);

        // Update Search History Record
        if (empty($searchhistory)) {
            (new SearchHistory())->saveUserSearchParameters($_POST);
        } else {
            (new SearchHistory())->updateUserSearchParameters($_POST);
        }

        $businessfacts = $this->getBusinessFactsPage($user_subscription_start_date, $user_subscription_end_date,);
        $searchhistory = (new SearchHistory())->getUsersPreviousSearchParameters($this->current_user_serial, $subscription_serial);

        // Populate Entry Dates with what was searched for if provided or users entry dates if no entry dates were provided

        $searchhistory->record->searchhistory_parameters->businessfact_entry_date_from = $businessfacts->record->businessfact_entry_date_from;
        $searchhistory->record->searchhistory_parameters->businessfact_entry_date_to = $businessfacts->record->businessfact_entry_date_to;

        $popovers  = (new Popovers())->getPopovers();
        $dropdowns = (new Dropdowns())->getDropdowns();
        $siccodes  = (new SICCodes())->getSICCodes();
        $cities    = (new Cities())->getSubscriptionCities($subscription_serial);
        $counties  = (new Counties())->getSubscriptionCounties($subscription_serial);

        $availablerecordcount = $this->userTotalAvailableRecordCount($user_subscription_start_date, $user_subscription_end_date);

        $XML = $this->mergeXML($businessfacts, "<XML/>");
        $XML = $this->mergeXML($subscription, $XML);
        $XML = $this->mergeXML($popovers, $XML);
        $XML = $this->mergeXML($dropdowns, $XML);
        $XML = $this->mergeXML($cities, $XML);
        $XML = $this->mergeXML($counties, $XML);
        $XML = $this->mergeXML($siccodes, $XML);
        $XML = $this->mergeXML($searchhistory, $XML);
        $XML = $this->mergeXML($availablerecordcount, $XML);

        $content = $this->applyXSL($XML, $this->getXSL("searchBusinessFacts"));
        $this->displayContent($content);
    }

    // ===================================
    // Return a Business Facts Page Object
    // ===================================

    public function getBusinessFactsPage($user_subscription_start_date, $user_subscription_end_date) {

        // Pagination settings
        $pagination = filter_input(INPUT_POST, "pagination") ?: "";
        $this->pagination_size = filter_input(INPUT_POST, "businessfacts_per_page", FILTER_SANITIZE_NUMBER_INT) ?: $this->pagination_size;

        // Connect to DB
        $connection = $this->connect();
        $filters = $this->buildBusinessFactsFilters($user_subscription_start_date, $user_subscription_end_date);

        // Record Count
        $SQL = "select count(*) as count
                  from view_businessfacts
                {$filters['where']}";

        $statement = $connection->prepare($SQL);
        $this->bindBusinessFactsParams($statement, $filters['params']);
        $statement->execute();

        $recordset = $statement->fetch(PDO::FETCH_ASSOC);
        $count = $recordset ? (int) $recordset["count"] : 0;

        // === PAGINATION LOGIC ===
        switch ($pagination) {
            case "first": $this->pagination_page = 1;
                break;
            case "previous": $this->pagination_page = max($this->pagination_page - 1, 1);
                break;
            case "next": $this->pagination_page = min($this->pagination_page + 1, ceil($count / $this->pagination_size));
                break;
            case "last": $this->pagination_page = (int) ceil($count / $this->pagination_size);
                break;
        }

        $offset = max(0, ($this->pagination_page - 1) * $this->pagination_size);
        $start = $offset + 1;
        $end = min($count, $start + $this->pagination_size - 1);

        // === DATA QUERY ===
        $SQL = "select view_businessfacts.*
                  from view_businessfacts 
                {$filters['where']}";

        if ($pagination !== 'off') {
            $SQL .= " limit :limit
                     offset :offset";
        }

//        echo $SQL;
//        die();

        $statement = $connection->prepare($SQL);
        $this->bindBusinessFactsParams($statement, $filters['params']);

        if ($pagination !== 'off') {
            $statement->bindValue(":limit", $this->pagination_size, PDO::PARAM_INT);
            $statement->bindValue(":offset", $offset, PDO::PARAM_INT);
        }

        $statement->execute();

        $businessfacts = $this->getTableXML("businessfacts", $statement);

        // Add searched entry_dates for search history

        $businessfacts->record->businessfact_entry_date_from = !empty($filters['entry_from']) ? date('m/d/Y', strtotime($filters['entry_from'])) : date('m/d/Y', strtotime($filters['subscription_start']));
        $businessfacts->record->businessfact_entry_date_to   = !empty($filters['entry_to'])   ? date('m/d/Y', strtotime($filters['entry_to']))   : date('m/d/Y', strtotime($filters['subscription_end']));

        // Insert Pagination Attributes
        $businessfacts->addAttribute("count", $count);
        $businessfacts->addAttribute("start", $start);
        $businessfacts->addAttribute("end", $end);
        $businessfacts->addAttribute("page", $this->pagination_page);

        $_SESSION[self::PAGINATION_PAGE] = $this->pagination_page;

        return $businessfacts;
    }

    private function buildBusinessFactsFilters($user_subscription_start_date, $user_subscription_end_date) {

        $businessfact_company             = trim((string) filter_input(INPUT_POST, "businessfact_company")) ?: "";
        $businessfact_zip                 = trim((string) filter_input(INPUT_POST, "businessfact_zip")) ?: "";
        $businessfact_home_based_business = trim((string) filter_input(INPUT_POST, "businessfact_home_based_business")) ?: "";
        $businessfact_employee_count_from = trim((string) filter_input(INPUT_POST, "businessfact_employee_count_from")) ?: "";
        $businessfact_employee_count_to   = trim((string) filter_input(INPUT_POST, "businessfact_employee_count_to")) ?: "";
        $businessfact_entry_date_from     = trim((string) filter_input(INPUT_POST, "businessfact_entry_date_from")) ?: "";
        $businessfact_entry_date_to       = trim((string) filter_input(INPUT_POST, "businessfact_entry_date_to")) ?: "";
        $businessfact_lease_end_from      = trim((string) filter_input(INPUT_POST, "businessfact_lease_end_from")) ?: "";
        $businessfact_lease_end_to        = trim((string) filter_input(INPUT_POST, "businessfact_lease_end_to")) ?: "";

        // Determine real date range based upon users subscription start date
        $oneYearAgo = date('Y-m-d', strtotime('-1 year'));
        if ($user_subscription_start_date <= $oneYearAgo) {
            $user_subscription_start_date = $oneYearAgo;
        }

        if ($user_subscription_end_date < $user_subscription_start_date) {
            $this->displayNotice("Your authorized access period is invalid (end date is before start date).");
            return;
        }

        $entryFrom = $this->normalizeBusinessFactsDate($businessfact_entry_date_from);
        $entryTo = $this->normalizeBusinessFactsDate($businessfact_entry_date_to);
        $leaseEndFrom = $this->normalizeBusinessFactsDate($businessfact_lease_end_from);
        $leaseEndTo = $this->normalizeBusinessFactsDate($businessfact_lease_end_to);

        $clauses = [];
        $params = [];

        $clauses[] = "view_businessfacts.businessfact_entry_date BETWEEN :subscription_start AND :subscription_end";
        $params[':subscription_start'] = $user_subscription_start_date;
        $params[':subscription_end'] = $user_subscription_end_date;

        $allowedCountyIds = $this->getBusinessFactsSubscriptionCountyIds();
        $requestedCountyIds = $this->getBusinessFactsPostIds("businessfact_county");
        $countyIds = $requestedCountyIds ? array_values(array_intersect($requestedCountyIds, $allowedCountyIds)) : $allowedCountyIds;

        $this->addBusinessFactsInClause($clauses, $params, "view_businessfacts.businessfact_county", $countyIds, "county_");

        if ($businessfact_company !== '') {
            $clauses[] = "view_businessfacts.businessfact_company REGEXP :businessfact_company";
            $params[':businessfact_company'] = $businessfact_company;
        }

        if ($businessfact_zip !== '') {
            $clauses[] = "view_businessfacts.businessfact_zip REGEXP :businessfact_zip";
            $params[':businessfact_zip'] = $businessfact_zip;
        }

        $this->addBusinessFactsInClause($clauses, $params, "view_businessfacts.businessfact_city", $this->getBusinessFactsPostIds("businessfact_city"), "city_");
        $this->addBusinessFactsInClause($clauses, $params, "view_businessfacts.businessfact_business_class", $this->getBusinessFactsPostIds("businessfact_business_class"), "class_");
        $this->addBusinessFactsInClause($clauses, $params, "view_businessfacts.businessfact_action", $this->getBusinessFactsPostIds("businessfact_action"), "action_");
        $this->addBusinessFactsInClause($clauses, $params, "view_businessfacts.businessfact_sic_code", $this->getBusinessFactsPostValues("businessfact_sic_code"), "sic_");

        if ($businessfact_home_based_business === 'Y' || $businessfact_home_based_business === 'N') {
            $clauses[] = "view_businessfacts.businessfact_home_based_business = :businessfact_home_based_business";
            $params[':businessfact_home_based_business'] = $businessfact_home_based_business;
        }

        if ($businessfact_employee_count_from !== '' && ctype_digit($businessfact_employee_count_from)) {
            $businessfact_employee_count_from = (int) $businessfact_employee_count_from;
        } else {
            $businessfact_employee_count_from = null;
        }

        if ($businessfact_employee_count_to !== '' && ctype_digit($businessfact_employee_count_to)) {
            $businessfact_employee_count_to = (int) $businessfact_employee_count_to;
        } else {
            $businessfact_employee_count_to = null;
        }

        if ($businessfact_employee_count_from !== null && $businessfact_employee_count_to !== null) {
            if ($businessfact_employee_count_from > $businessfact_employee_count_to) {
                $tmp = $businessfact_employee_count_from;
                $businessfact_employee_count_from = $businessfact_employee_count_to;
                $businessfact_employee_count_to = $tmp;
            }

            $clauses[] = "view_businessfacts.businessfact_employee_count BETWEEN :employee_count_from AND :employee_count_to";
            $params[':employee_count_from'] = $businessfact_employee_count_from;
            $params[':employee_count_to'] = $businessfact_employee_count_to;
        } elseif ($businessfact_employee_count_from !== null) {
            $clauses[] = "view_businessfacts.businessfact_employee_count = :employee_count";
            $params[':employee_count'] = $businessfact_employee_count_from;
        } elseif ($businessfact_employee_count_to !== null) {
            $clauses[] = "view_businessfacts.businessfact_employee_count = :employee_count";
            $params[':employee_count'] = $businessfact_employee_count_to;
        }

        if ($entryFrom !== null || $entryTo !== null) {
            if ($entryFrom !== null && $entryTo === null) {
                $entryTo = $entryFrom;
            }
            if ($entryTo !== null && $entryFrom === null) {
                $entryFrom = $entryTo;
            }

            if ($entryFrom > $entryTo) {
                $tmp = $entryFrom;
                $entryFrom = $entryTo;
                $entryTo = $tmp;
            }

            if ($entryFrom < $user_subscription_start_date || $entryTo > $user_subscription_end_date) {
                $this->displayNotice("The Entry date or date range {$this->current_user_name} selected is outside your authorized access period.");
                return;
            }

            $clauses[] = "view_businessfacts.businessfact_entry_date BETWEEN :entry_from AND :entry_to";
            $params[':entry_from'] = $entryFrom;
            $params[':entry_to'] = $entryTo;
        }

        if ((string) ($_SESSION['subscription_serial'] ?? '') === '10' && ($leaseEndFrom !== null || $leaseEndTo !== null)) {
            if ($leaseEndFrom !== null && $leaseEndTo === null) {
                $leaseEndTo = $leaseEndFrom;
            }
            if ($leaseEndTo !== null && $leaseEndFrom === null) {
                $leaseEndFrom = $leaseEndTo;
            }

            if ($leaseEndFrom > $leaseEndTo) {
                $tmp = $leaseEndFrom;
                $leaseEndFrom = $leaseEndTo;
                $leaseEndTo = $tmp;
            }

            $clauses[] = "view_businessfacts.businessfact_lease_end BETWEEN :lease_end_from AND :lease_end_to";
            $params[':lease_end_from'] = $leaseEndFrom;
            $params[':lease_end_to'] = $leaseEndTo;
        }

        return [
            'where' => "where " . implode(" and ", $clauses),
            'params' => $params,
            'entry_from' => $entryFrom,
            'entry_to' => $entryTo,
            'subscription_start' => $user_subscription_start_date,
            'subscription_end' => $user_subscription_end_date,
        ];
    }

    private function getBusinessFactsSubscriptionCountyIds() {

        $counties = (new Counties())->getSubscriptionCounties($_SESSION['subscription_serial']);
        $countyIds = [];

        foreach ($counties as $county) {
            $countyId = trim((string) $county->subscriptioncounty_county);
            if (ctype_digit($countyId)) {
                $countyIds[] = (int) $countyId;
            }
        }

        return array_values(array_unique($countyIds));
    }

    private function getBusinessFactsPostIds($name) {

        $values = $this->getBusinessFactsPostValues($name);
        $ids = [];

        foreach ($values as $value) {
            if (ctype_digit((string) $value)) {
                $ids[] = (int) $value;
            }
        }

        return array_values(array_unique($ids));
    }

    private function getBusinessFactsPostValues($name) {

        $value = $_POST[$name] ?? $_POST[$name . "_"] ?? "";

        if (is_array($value)) {
            $values = $value;
        } else {
            $values = explode(',', (string) $value);
        }

        $clean = [];
        foreach ($values as $item) {
            $item = trim((string) $item);
            if ($item !== '' && preg_match('/^[A-Za-z0-9_-]+$/', $item)) {
                $clean[] = $item;
            }
        }

        return array_values(array_unique($clean));
    }

    private function addBusinessFactsInClause(&$clauses, &$params, $field, $values, $paramPrefix) {

        if (!$values) {
            if ($field === "view_businessfacts.businessfact_county") {
                $clauses[] = "1 = 0";
            }
            return;
        }

        $placeholders = [];
        foreach ($values as $index => $value) {
            $placeholder = ":{$paramPrefix}{$index}";
            $placeholders[] = $placeholder;
            $params[$placeholder] = $value;
        }

        $clauses[] = "{$field} IN (" . implode(',', $placeholders) . ")";
    }

    private function bindBusinessFactsParams($statement, $params) {

        foreach ($params as $name => $value) {
            $type = is_int($value) ? PDO::PARAM_INT : PDO::PARAM_STR;
            $statement->bindValue($name, $value, $type);
        }
    }

    private function normalizeBusinessFactsDate($date) {

        if ($date === '') {
            return null;
        }

        $timestamp = strtotime($date);
        return $timestamp === false ? null : date("Y-m-d", $timestamp);
    }

    // ==================================
    // Users Total Available Record Count
    // ==================================

    public function userTotalAvailableRecordCount($user_subscription_start_date, $user_subscription_end_date) {

        // determine real date range based upon users subscription start date

        if ($user_subscription_start_date <= date('Y-m-d', strtotime('-1 year'))) {
            $user_subscription_start_date = date('Y-m-d', strtotime('-1 year'));
        }

        $clauses = [
            "view_businessfacts.businessfact_entry_date between :subscription_start and :subscription_end",
        ];

        $params = [
            ':subscription_start' => $user_subscription_start_date,
            ':subscription_end' => $user_subscription_end_date,
        ];

        $this->addBusinessFactsInClause($clauses, $params, "view_businessfacts.businessfact_county", $this->getBusinessFactsSubscriptionCountyIds(), "county_");

        $SQL = "select count(*) as count
                  from view_businessfacts
                 where " . implode(" and ", $clauses);

        $statement = $this->connect()->prepare($SQL);
        $this->bindBusinessFactsParams($statement, $params);
        $statement->execute();

        return $this->getTableXML("userstotalavailablerecords", $statement);
    }

    // =====================
    // Search Business Facts
    // =====================

    public function businessFactsBasic() {

        $step = filter_input(INPUT_POST, "step") ?: "prompt";

        switch ($step) {

            case "prompt":

                $dateranges = $this->getDateRangeXML();

                $content = $this->applyXSL($dateranges, $this->getXSL("businessFactsBasic"));

                $this->displayContent($content);

                break;

            case "results":

                $week_start_raw = trim((string) filter_input(INPUT_POST, 'week_start'));
                $week_end_raw = trim((string) filter_input(INPUT_POST, 'week_end'));

                $week_start_db = $week_start_raw !== '' ? date('Y-m-d', strtotime($week_start_raw)) : null;
                $week_end_db = $week_end_raw !== '' ? date('Y-m-d', strtotime($week_end_raw)) : null;

                $businessfacts = $this->getBusinessFactsBasic($week_start_db, $week_end_db);

                $dateranges = $this->getDateRangeXML();

                $searched = $dateranges->addChild('searchedranges');
                $searched->addChild('week_start', $week_start_db ? date('m/d/Y', strtotime($week_start_db)) : '');
                $searched->addChild('week_end', $week_end_db ? date('m/d/Y', strtotime($week_end_db)) : '');

                $xml = $this->mergeXML($businessfacts, '<XML/>');
                $xml = $this->mergeXML($dateranges, $xml);

                $content = $this->applyXSL($xml, $this->getXSL("businessFactsBasic"));

                $this->displayContent($content);

                break;

            default:

                $this->displayHome();
        }
    }

    public function getDateRangeXML() {

        $ranges = $this->getFridayThursdayRanges(2, new DateTimeImmutable('today'));

        $xml = new SimpleXMLElement('<ranges/>');

        $labels = [
            0 => 'week_prior',
            1 => 'two_weeks_prior',
        ];

        foreach ($ranges as $i => $range) {
            $name = $labels[$i] ?? 'prior_' . ($i + 1);

            $node = $xml->addChild($name);

            $start = date('m/d/Y', strtotime($range['start']));
            $end = date('m/d/Y', strtotime($range['end']));

            $node->addChild('start', $start);
            $node->addChild('end', $end);
        }

        $xml->subscription_serial = $_SESSION['subscription_serial'];

        return $xml;
    }

    public function getFridayThursdayRanges(int $weeks = 2, ?DateTimeInterface $today = null): array {

        $today = $today ? DateTimeImmutable::createFromInterface($today) : new DateTimeImmutable('today');

        // "last thursday" gives the most recent Thursday strictly before today.
        // If today IS Thursday, we want today as the end date.
        $end = ($today->format('N') === '4') ? $today : $today->modify('last thursday');

        $ranges = [];
        for ($i = 0; $i < $weeks; $i++) {
            $start = $end->modify('-6 days');
            $ranges[] = [
                'start' => $start->format('Y-m-d'),
                'end' => $end->format('Y-m-d'),
            ];
            $end = $start->modify('-1 day'); // move to previous block
        }

        return $ranges;
    }

    // ===============================
    // Return an Business Facts Object
    // ===============================

    public function getBusinessFactsBasic($week_start, $week_end) {

        $SQL = "select view_businessfacts.*
                  from view_businessfacts
                 where view_businessfacts.businessfact_entry_date between '{$week_start}' and '{$week_end}'
              order by view_businessfacts.businessfact_county_name_verbose, view_businessfacts.businessfact_zip, view_businessfacts.businessfact_company";

        return $this->getTable("businessfacts", $SQL);
    }
}

?>
