<?php

declare(strict_types=1);

$basePath = realpath(__DIR__ . '/..');
require_once $basePath . '/vendor/autoload.php';

use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;

// =====================
// Process_ExportRequest
// =====================

class Process_ExportRequest {

    // Variables

    private $log = "";
    private $settings = "";
    private $connection = "";

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

    public function __construct() {

        $basePath = realpath(__DIR__ . '/..');
        $logfile = $basePath . "/scripts/exportrequests.log";

        // ==========================
        // Log Rotation (50 MB limit)
        // ==========================

        $maxLogSize = 50 * 1024 * 1024; // 50 MB

        if (file_exists($logfile) && filesize($logfile) >= $maxLogSize) {

            $rotated = $logfile . '-';
            rename($logfile, $rotated);
        }

        $this->log = fopen($logfile, "a");

        if ($this->log === false) {

            error_log("Process_ExportRequest : Unable to open log file");
            exit();
        }

        $this->log(str_repeat("*", 50));

        // Get Settings

        $this->settings = new SimpleXMLElement((file_exists($basePath . "/xml/settings.xml") ? file_get_contents($basePath . "/xml/settings.xml") : "<settings/>"));

        if ($this->settings->count() === 0) {

            error_log("Process_ExportRequest : Unable to initialize settings");
            exit();
        }

        // Open Database Connections

        $this->connection = $this->connect();
    }

    // ================================================
    // Return a New DEC-DR Database Database Connection
    // ================================================

    public function connect() {

        if ($this->settings->Environment == 'Production') {
            $host = 'localhost';
        } else {
            $host = '192.168.1.190';
        }

        try {

            $database = (string) $this->settings->DatabaseName;
            $user = (string) $this->settings->DatabaseUser;
            $password = (string) $this->settings->DatabasePassword;

            $connection = new PDO("mysql:host={$host};dbname={$database};charset=utf8", $user, $password);

            $connection->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
            $connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        } catch (PDOException | Exception $e) {

            trigger_error($e->getMessage());
        }

        return $connection;
    }

    // =============================
    // Log a message to the Log File
    // =============================

    private function log($message = "") {

        fwrite($this->log, date("Y-m-d H:i A") . " : " . "{$message} \n");
    }

    // ===================
    // Process Parcel Data
    // ===================

    public function process() {

        $this->log("Starting All Record Export Job");

        $this->runExportJob();

        $this->purgeExportJobs();

        $this->log("Completed All Record Export Job");
    }

    // ==============
    // Run Export Job
    // ==============

    private function runExportJob() {

        $this->log("Processing Export Job");

        $nextjob = $this->claimNextJob();

        if (!$nextjob) {
            $this->log("Nothing to do.");
            return;
        }

        $exportjob_serial = (int) $nextjob['exportjob_serial'];
        $exportjob_subscription = (int) $nextjob['exportjob_subscription'];
        $exportjob_file_name = (string) $nextjob['exportjob_file_name'];
        $exportjob_file_type = (string) $nextjob['exportjob_file_type'];
        $exportjob_creator = (string) $nextjob['exportjob_creator'];

        $usersubscription = $this->getUserSubscription($exportjob_subscription, $exportjob_creator);
        $subscription = $this->getSubscription($exportjob_subscription);

        $user_subscription_start_date = $usersubscription[0]['usersubscription_start_date'];
        $user_subscription_end_date = $usersubscription[0]['usersubscription_end_date'];

        $counties = $this->getSubscriptionCounties($exportjob_subscription);

        try {

            switch ($exportjob_subscription) {

                case 1:
                case 2:
                case 3:
                case 4:

                    // Permit Facts
                    $permitfacts = $this->getPermitFactsFullDataset($user_subscription_start_date, $user_subscription_end_date, $counties);
                    $result = $this->generatePermitFactsExportFile($exportjob_file_name, $exportjob_file_type, $permitfacts, $subscription);

                    break;

                case 5:

                    // Planning/Zoning Facts
                    $pzfacts = $this->getPZFactsFullDataset($user_subscription_start_date, $user_subscription_end_date, $counties);
                    $result = $this->generatePZFactsExportFile($exportjob_file_name, $exportjob_file_type, $pzfacts, $subscription);

                    break;

                case 7:
                case 10:

                    // Business Facts

                    $businessfacts = $this->getBusinessFactsFullDataset($user_subscription_start_date, $user_subscription_end_date, $counties);
                    $result = $this->generateBusinessFactsExportFile($exportjob_file_name, $exportjob_file_type, $businessfacts, $subscription);

                    break;
            }

            $exportjob_download_token = hash('sha256', random_bytes(32));

            $SQL = "UPDATE exportjobs
                   SET exportjob_status          = 'complete',
                       exportjob_completed       = NOW(),
                       exportjob_file_path       = :exportjob_file_path,
                       exportjob_file_type       = :exportjob_file_type,
                       exportjob_download_token  = :exportjob_download_token
                 WHERE exportjob_serial          = :exportjob_serial ";

            $update = $this->connect()->prepare($SQL);

            $update->execute([':exportjob_file_path' => $result['exportjob_file_path'], ":exportjob_download_token" => $exportjob_download_token,
                ':exportjob_serial' => $exportjob_serial, ':exportjob_file_type' => $result['exportjob_file_type']]);
        } catch (Throwable $e) {

            $SQL = "UPDATE exportjobs
                       SET exportjob_status          = 'failed',
                           exportjob_completed       = NOW(),
                           exportjob_error_message   = :exportjob_error_message
                     WHERE exportjob_serial          = :exportjob_serial";

            $fail = $this->connect()->prepare($SQL);

            $fail->execute([':exportjob_serial' => $exportjob_serial, ':exportjob_error_message' => $e->getMessage()]);

            // Notify Admins of error
            $this->sendWebHookAlert("Export Job {$exportjob_serial} has failed. Check Error Logs for details.");
            $this->email_admins("Export Job Failure.", "Export Job {$exportjob_serial} has failed. Check Error Logs for details.");
        }
    }

    // =================================
    // Purge Exports Older then 24 Hours
    // =================================

    private function purgeExportJobs() {

        $this->log("Purging Export Jobs Database Files");

        $this->purgeFiles();
    }

    // =======================
    // Claim Next Job in Queue
    // =======================

    private function claimNextJob() {

        $this->connection->beginTransaction();

        $SQL = "SELECT view_exportjobs.* 
                  FROM view_exportjobs
                 WHERE view_exportjobs.exportjob_status = 'queued'
              ORDER BY view_exportjobs.exportjob_created ASC
                 LIMIT 1 FOR UPDATE";

        $statement = $this->connection->prepare($SQL);

        $statement->execute();

        $job = $statement->fetch(PDO::FETCH_ASSOC);

        if (!$job) {
            $this->connection->commit();
            return null;
        }

        $SQL = "UPDATE exportjobs
                   SET exportjob_status = 'running'
                 WHERE exportjob_serial = :exportjob_serial";

        $statement = $this->connection->prepare($SQL);

        $statement->execute([':exportjob_serial' => (int) $job['exportjob_serial']]);

        $this->connection->commit();

        return $job;
    }

    // ==================================
    // Return a Users Subscription Object
    // ==================================

    private function getUserSubscription($subscription, $user) {

        $SQL = "select view_usersubscriptions.*
                  from view_usersubscriptions
                 where view_usersubscriptions.usersubscription_user = {$user}
                   and view_usersubscriptions.usersubscription_subscription = {$subscription}";

        $usersubscription = $this->connect()->query($SQL)->fetchAll(PDO::FETCH_ASSOC);

        return $usersubscription;
    }

    // ============================
    // Return a Subscription Object
    // ============================

    private function getSubscription($subscription) {

        $SQL = "select view_subscriptions.*
                  from view_subscriptions
                 where subscription_serial = {$subscription}";

        $subscription = $this->connect()->query($SQL)->fetchAll(PDO::FETCH_ASSOC);

        return $subscription;
    }

    // ==============================
    // Get Permit Facsts Full Dataset
    // ==============================

    private function getPermitFactsFullDataset($user_subscription_start_date, $user_subscription_end_date, $counties) {

        set_time_limit(0);

        // 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'));
        }

        $SQL = "select view_permitfacts.*
                  from view_permitfacts 
                 where view_permitfacts.permitfact_entry_date between '{$user_subscription_start_date}' and '{$user_subscription_end_date}'
                   and view_permitfacts.permitfact_county in ($counties)";

        $permitfacts = $this->connect()->query($SQL)->fetchAll(PDO::FETCH_ASSOC);

        return $permitfacts;
    }

    // ======================================
    // Get Planning/Zoning Facts Full Dataset
    // ======================================

    private function getPZFactsFullDataset($user_subscription_start_date, $user_subscription_end_date, $counties) {

        set_time_limit(0);

        // 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'));
        }

        $SQL = "select view_pzfacts.*
                  from view_pzfacts 
                 where view_pzfacts.pzfact_entry_date between '{$user_subscription_start_date}' and '{$user_subscription_end_date}'
                   and view_pzfacts.pzfact_county in ($counties)";

        $pzfacts = $this->connect()->query($SQL)->fetchAll(PDO::FETCH_ASSOC);

        return $pzfacts;
    }

    // ===============================
    // Get Business Facts Full Dataset
    // ===============================

    private function getBusinessFactsFullDataset($user_subscription_start_date, $user_subscription_end_date, $counties) {

        set_time_limit(0);

        // 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'));
        }

        $SQL = "select view_businessfacts.*
                  from view_businessfacts 
                 where view_businessfacts.businessfact_entry_date between '{$user_subscription_start_date}' and '{$user_subscription_end_date}'
                   and view_businessfacts.businessfact_county in ($counties)";

        $businessfacts = $this->connect()->query($SQL)->fetchAll(PDO::FETCH_ASSOC);

        return $businessfacts;
    }

    // ========================
    // Return a Counties Object
    // ========================

    private function getSubscriptionCounties($subscription_serial) {

        $SQL = "select view_subscriptioncounties.*
                  from view_subscriptioncounties
                 where view_subscriptioncounties.subscriptioncounty_subscription = '{$subscription_serial}'";

        $counties = $this->connect()->query($SQL)->fetchAll(PDO::FETCH_ASSOC);

        $countyNames = [];

        foreach ($counties as $county) {

            $countyNames[] = $county['subscriptioncounty_county'];
        }

        $counties = implode(', ', $countyNames);

        return $counties;
    }

    // ====================
    // Purges Files on Disk
    // ====================

    private function purgeFiles() {

        $SQL = "select * 
                  from exportjobs 
                 where exportjobs.exportjob_created < (NOW() - INTERVAL 24 HOUR)";

        $exportfiles = $this->connect()->query($SQL)->fetchAll(PDO::FETCH_ASSOC);

        if (count($exportfiles) == 0) {
            $this->log("Nothing To Purge");
            return;
        }

        foreach ($exportfiles as $files) {

            $filename = (string) $files['exportjob_file_path'];

            unlink($filename);
        }

        $baseDir = './exports';

        foreach (new DirectoryIterator($baseDir) as $item) {

            if ($item->isDot() || !$item->isDir()) {
                continue;
            }

            $folderPath = $item->getPathname();

            // Check if directory is empty
            $files = scandir($folderPath);

            if ($files !== false && count($files) === 2) {
                rmdir($folderPath);
            }
        }

        $this->log("Purging Export Jobs Database Records");

        $SQL = "delete from exportjobs
                 where exportjobs.exportjob_created < (NOW() - INTERVAL 24 HOUR)";

        $this->connection->query($SQL);
    }

    // ===================================
    // Generate Business Facts Export File
    // ===================================

    private function generateBusinessFactsExportFile($exportjob_file_name, $exportjob_file_type, $businessfacts, $usersubscription) {

        $job_code = $this->getUniqueJobCode();

        //Create a dedicated job folder
        $base_directory = "./exports/{$job_code}";

        if (!is_dir($base_directory)) {
            mkdir($base_directory, 0777, true);
        }

        $exportjob_file_path = "{$base_directory}/{$exportjob_file_name}";

        switch ($exportjob_file_type) {

            case "CSV":

                // Create a dedicated job folder
                $base_directory = "./exports/{$job_code}";

                if (!is_dir($base_directory)) {
                    mkdir($base_directory, 0777, true);
                }

                $exportjob_file_path = "{$base_directory}/{$exportjob_file_name}";

                $this->log($exportjob_file_path);

                $file_pointer = (fopen($exportjob_file_path, 'w'));

                if (!$file_pointer) {
                    throw new RuntimeException("Unable to write: {$exportjob_file_path}");
                }

                // Add CSV headers

                $header_array = [
                    'businessfact_legacy_id', 'businessfact_company', 'businessfact_address_one', 'businessfact_address_two', 'businessfact_city_verbose', 'businessfact_state',
                    'businessfact_zip', 'businessfact_county_name_verbose', 'businessfact_phone', 'businessfact_fax', 'businessfact_email_address', 'businessfact_business_class_verbose',
                    'businessfact_action_verbose', 'businessfact_employee_count', 'businessfact_inception', 'businessfact_sic_code', 'businessfact_siccode_description_verbose', 'businessfact_contact_name',
                    'businessfact_contact_title', 'businessfact_license_date_verbose', 'businessfact_home_based_business', 'businessfact_year_established', 'businessfact_entry_date_verbose'
                ];

                fputcsv($file_pointer, $header_array, ',', '"', '\\');

                foreach ($businessfacts as $businessfact) {

                    fputcsv($file_pointer, $businessfact, ',', '"', '\\');
                }

                fclose($file_pointer);

                break;

            case "XLSX":

                $this->createExcelExport_BusinessFacts($businessfacts, $base_directory, $exportjob_file_name);

                break;

            case "PDF":

                $this->createPDFExport_BusinessFacts($businessfacts, $usersubscription, $base_directory, $exportjob_file_name);

                break;
        }

        $exportjob_file_path = str_replace('../exports/', './exports/', $exportjob_file_path);
        $exportjob_file_type = strtoupper(pathinfo($exportjob_file_path, PATHINFO_EXTENSION));

        return ['exportjob_file_name' => $exportjob_file_name, 'exportjob_file_path' => $exportjob_file_path, 'exportjob_file_type' => $exportjob_file_type];
    }

    // =================================
    // Generate Permit Facts Export File
    // =================================

    private function generatePermitFactsExportFile($exportjob_file_name, $exportjob_file_type, $permitfacts, $usersubscription) {

        $job_code = $this->getUniqueJobCode();

        //Create a dedicated job folder
        $base_directory = "./exports/{$job_code}";

        if (!is_dir($base_directory)) {
            mkdir($base_directory, 0777, true);
        }

        $exportjob_file_path = "{$base_directory}/{$exportjob_file_name}";

        switch ($exportjob_file_type) {

            case "CSV":

                // Create a dedicated job folder
                $base_directory = "./exports/{$job_code}";

                if (!is_dir($base_directory)) {
                    mkdir($base_directory, 0777, true);
                }

                $exportjob_file_path = "{$base_directory}/{$exportjob_file_name}";

                $file_pointer = (fopen($exportjob_file_path, 'w'));

                if (!$file_pointer) {
                    throw new RuntimeException("Unable to write: {$exportjob_file_path}");
                }

                // Add CSV headers

                $header_array = [
                    'permitfact_serial', 'permitfact_entry_date_verbose', 'permitfact_permit_number', 'permitfact_permit_date_verbose', 'permitfact_project_address', 'permitfact_project_name',
                    'permitfact_project_type_verbose', 'project_city_verbose', 'permitfact_project_state', 'permitfact_project_zip', 'permitfact_company', 'permitfact_address',
                    'permitfact_city_verbose', 'permitfact_state', 'permitfact_zip', 'permitfact_phone', 'permitfact_size', 'permitfact_value', 'permitfact_work_type_verbose',
                    'permitfact_building_type_verbose', 'permitfact_county_name_verbose', 'permitfact_lot_block', 'permitfact_dist_ll', 'permitfact_owner_name', 'permitfact_owner_address',
                    'owner_city_verbose', 'permitfact_owner_state', 'permitfact_owner_zip', 'permitfact_owner_phone'
                ];

                fputcsv($file_pointer, $header_array, ',', '"', '\\');

                foreach ($permitfacts as $permitfact) {

                    fputcsv($file_pointer, $permitfact, ',', '"', '\\');
                }

                fclose($file_pointer);

                break;

            case "XLSX":

                $result = $this->createExcelExport_PermitFacts($permitfacts, $base_directory, $exportjob_file_name);

                break;

            case "PDF":

                $this->createPDFExport_PermitFacts($permitfacts, $usersubscription, $base_directory, $exportjob_file_name);

                break;
        }

        $exportjob_file_path = str_replace('../exports/', './exports/', $exportjob_file_path);
        $exportjob_file_type = strtoupper(pathinfo($exportjob_file_path, PATHINFO_EXTENSION));

        return ['exportjob_file_name' => $exportjob_file_name, 'exportjob_file_path' => $exportjob_file_path, 'exportjob_file_type' => $exportjob_file_type];
    }

    // ====================================
    // Generate Planning/Zoning Export File
    // ====================================

    private function generatePZFactsExportFile($exportjob_file_name, $exportjob_file_type, $pzfacts, $usersubscription) {


        // Get a job code and create a dedicated job folder
        $job_code = $this->getUniqueJobCode();
        $base_directory = "./exports/{$job_code}";

        if (!is_dir($base_directory)) {
            mkdir($base_directory, 0777, true);
        }

        $exportjob_file_path = "{$base_directory}/{$exportjob_file_name}";

        switch ($exportjob_file_type) {

            case "CSV":

                $file_pointer = (fopen($exportjob_file_path, 'w'));

                if (!$file_pointer) {
                    throw new RuntimeException("Unable to write: {$exportjob_file_path}");
                }

                // Add CSV headers

                $header_array = [
                    'pzfact_legacy_id', 'pzfact_county', 'pzfact_owner_name', 'pzfact_owner_state', 'pzfact_owner_address', 'pzfact_owner_phone',
                    'pzfact_owner_fax', 'pzfact_professional_name', 'pzfact_professional_state', 'pzfact_professional_address', 'pzfact_professional_phone', 'pzfact_professional_fax',
                    'pzfact_project_type_verbose', 'pzfact_project_name', 'pzfact_project_description', 'pzfact_project_address', 'pzfact_project_city', 'pzfact_action_code_verbose',
                    'pzfact_district', 'pzfact_land_lot', 'pzfact_block', 'pzfact_parcel_number', 'pzfact_lot', 'pzfact_action_date_verbose',
                    'pzfact_acres', 'pzfact_status', 'pzfact_entry_date_verbose'
                ];

                fputcsv($file_pointer, $header_array, ',', '"', '\\');

                foreach ($pzfacts as $pzfact) {

                    fputcsv($file_pointer, $pzfact, ',', '"', '\\');
                }

                fclose($file_pointer);

                break;

            case "XLSX":

                $this->createExcelExport_PZFacts($pzfacts, $base_directory, $exportjob_file_name);

                break;

            case "PDF":

                print_r($pzfacts);

                $this->createPDFExport_PZFacts($pzfacts, $usersubscription, $base_directory, $exportjob_file_name);

                break;
        }

        $exportjob_file_path = str_replace('../exports/', './exports/', $exportjob_file_path);

        $exportjob_file_type = strtoupper(pathinfo($exportjob_file_path, PATHINFO_EXTENSION));

        return ['exportjob_file_name' => $exportjob_file_name, 'exportjob_file_path' => $exportjob_file_path, 'exportjob_file_type' => $exportjob_file_type];
    }

    // ==========================
    // Creates Permit Facts Excel
    // ==========================

    private function createExcelExport_PermitFacts($permitfacts, $exportjob_base_directory, $exportjob_file_name) {

        // Create the Spreadsheet.

        $spreadsheet = new Spreadsheet();

        $spreadsheet->getProperties()->setCreator("DEC International")->setTitle("PermitFacts")->setSubject("PermitFacts")
                ->setDescription("PermitFacts")->setKeywords("PermitFacts")->setCategory("PermitFacts");

        // Create the Base Sheet.

        $spreadsheet->setActiveSheetIndex(0);
        $spreadsheet->getActiveSheet()->setTitle("Permits");

        // Format the Cells and Create the Headings.

        $spreadsheet->getActiveSheet()->getColumnDimension("A")->setAutoSize(true);
        $spreadsheet->getActiveSheet()->getColumnDimension("B")->setAutoSize(true);
        $spreadsheet->getActiveSheet()->getColumnDimension("C")->setAutoSize(true);
        $spreadsheet->getActiveSheet()->getColumnDimension("D")->setAutoSize(true);
        $spreadsheet->getActiveSheet()->getColumnDimension("E")->setAutoSize(true);
        $spreadsheet->getActiveSheet()->getColumnDimension("F")->setAutoSize(true);
        $spreadsheet->getActiveSheet()->getColumnDimension("G")->setAutoSize(true);
        $spreadsheet->getActiveSheet()->getColumnDimension("H")->setAutoSize(true);
        $spreadsheet->getActiveSheet()->getColumnDimension("I")->setAutoSize(true);
        $spreadsheet->getActiveSheet()->getColumnDimension("J")->setAutoSize(true);
        $spreadsheet->getActiveSheet()->getColumnDimension("K")->setAutoSize(true);
        $spreadsheet->getActiveSheet()->getColumnDimension("L")->setAutoSize(true);
        $spreadsheet->getActiveSheet()->getColumnDimension("M")->setAutoSize(true);
        $spreadsheet->getActiveSheet()->getColumnDimension("N")->setAutoSize(true);
        $spreadsheet->getActiveSheet()->getColumnDimension("O")->setAutoSize(true);
        $spreadsheet->getActiveSheet()->getColumnDimension("P")->setAutoSize(true);
        $spreadsheet->getActiveSheet()->getColumnDimension("Q")->setAutoSize(true);
        $spreadsheet->getActiveSheet()->getColumnDimension("R")->setAutoSize(true);
        $spreadsheet->getActiveSheet()->getColumnDimension("S")->setAutoSize(true);
        $spreadsheet->getActiveSheet()->getColumnDimension("T")->setAutoSize(true);
        $spreadsheet->getActiveSheet()->getColumnDimension("U")->setAutoSize(true);
        $spreadsheet->getActiveSheet()->getColumnDimension("V")->setAutoSize(true);
        $spreadsheet->getActiveSheet()->getColumnDimension("W")->setAutoSize(true);
        $spreadsheet->getActiveSheet()->getColumnDimension("X")->setAutoSize(true);
        $spreadsheet->getActiveSheet()->getColumnDimension("Y")->setAutoSize(true);
        $spreadsheet->getActiveSheet()->getColumnDimension("Z")->setAutoSize(true);
        $spreadsheet->getActiveSheet()->getColumnDimension("AA")->setAutoSize(true);

        $spreadsheet->getActiveSheet()->getStyle("A1:AA1")->getFont()->setBold(true);
        $spreadsheet->getActiveSheet()->getStyle("A1:AA1")->getFill()->setFillType(\PhpOffice\PhpSpreadsheet\Style\Fill::FILL_SOLID)->getStartColor()->setARGB("D3D3D3");

        $spreadsheet->setActiveSheetIndex(0)->setCellValue("A1", "permitfact_serial")
                ->setCellValue("B1", "Entry Date")
                ->setCellValue("C1", "Permit Number")
                ->setCellValue("D1", "Permit Date")
                ->setCellValue("E1", "Project Address")
                ->setCellValue("F1", "Project Name")
                ->setCellValue("G1", "Project Type")
                ->setCellValue("H1", "Project City")
                ->setCellValue("I1", "Project State")
                ->setCellValue("J1", "Project Zip")
                ->setCellValue("K1", "Company")
                ->setCellValue("L1", "Address")
                ->setCellValue("M1", "City")
                ->setCellValue("N1", "State")
                ->setCellValue("O1", "Zip")
                ->setCellValue("P1", "Phone Number")
                ->setCellValue("Q1", "Size")
                ->setCellValue("R1", "Value")
                ->setCellValue("S1", "Work Type")
                ->setCellValue("T1", "Building Type")
                ->setCellValue("U1", "County")
                ->setCellValue("V1", "Owner Name")
                ->setCellValue("W1", "Owner Address")
                ->setCellValue("X1", "Owner City")
                ->setCellValue("Y1", "Owner State")
                ->setCellValue("Z1", "Owner Zip")
                ->setCellValue("AA1", "Owner Phone");

        $spreadsheet->getActiveSheet()->getStyle("A:A")->getNumberFormat()->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_NUMBER);
        $spreadsheet->getActiveSheet()->getStyle("C:C")->getNumberFormat()->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_TEXT);
        $spreadsheet->getActiveSheet()->getStyle("D:D")->getNumberFormat()->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_TEXT);
        $spreadsheet->getActiveSheet()->getStyle("E:E")->getNumberFormat()->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_TEXT);
        $spreadsheet->getActiveSheet()->getStyle("F:F")->getNumberFormat()->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_TEXT);
        $spreadsheet->getActiveSheet()->getStyle("G:G")->getNumberFormat()->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_TEXT);
        $spreadsheet->getActiveSheet()->getStyle("H:H")->getNumberFormat()->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_TEXT);
        $spreadsheet->getActiveSheet()->getStyle("I:I")->getNumberFormat()->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_TEXT);
        $spreadsheet->getActiveSheet()->getStyle("J:J")->getNumberFormat()->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_TEXT);
        $spreadsheet->getActiveSheet()->getStyle("K:K")->getNumberFormat()->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_TEXT);
        $spreadsheet->getActiveSheet()->getStyle("L:L")->getNumberFormat()->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_TEXT);
        $spreadsheet->getActiveSheet()->getStyle("M:M")->getNumberFormat()->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_TEXT);
        $spreadsheet->getActiveSheet()->getStyle("N:N")->getNumberFormat()->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_TEXT);
        $spreadsheet->getActiveSheet()->getStyle("O:O")->getNumberFormat()->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_TEXT);
        $spreadsheet->getActiveSheet()->getStyle("P:P")->getNumberFormat()->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_TEXT);
        $spreadsheet->getActiveSheet()->getStyle("Q:Q")->getNumberFormat()->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_TEXT);
        $spreadsheet->getActiveSheet()->getStyle("R:R")->getNumberFormat()->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_TEXT);
        $spreadsheet->getActiveSheet()->getStyle("S:S")->getNumberFormat()->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_TEXT);
        $spreadsheet->getActiveSheet()->getStyle("T:T")->getNumberFormat()->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_TEXT);
        $spreadsheet->getActiveSheet()->getStyle("U:U")->getNumberFormat()->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_TEXT);
        $spreadsheet->getActiveSheet()->getStyle("V:V")->getNumberFormat()->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_TEXT);
        $spreadsheet->getActiveSheet()->getStyle("W:W")->getNumberFormat()->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_TEXT);
        $spreadsheet->getActiveSheet()->getStyle("X:X")->getNumberFormat()->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_TEXT);
        $spreadsheet->getActiveSheet()->getStyle("Y:Y")->getNumberFormat()->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_TEXT);
        $spreadsheet->getActiveSheet()->getStyle("Z:Z")->getNumberFormat()->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_TEXT);
        $spreadsheet->getActiveSheet()->getStyle("AA:AA")->getNumberFormat()->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_TEXT);

        $spreadsheet->getActiveSheet()->freezePane("A2");

        // Create the Detail Rows.

        $row = 2;

        foreach ($permitfacts as $permitfact) {

            $spreadsheet->getActiveSheet()->setCellValue("A" . $row, (string) $permitfact['permitfact_serial']);
            $spreadsheet->getActiveSheet()->setCellValue("B" . $row, (string) $permitfact['permitfact_entry_date_verbose']);
            $spreadsheet->getActiveSheet()->setCellValue("C" . $row, (string) $permitfact['permitfact_permit_number']);
            $spreadsheet->getActiveSheet()->setCellValue("D" . $row, (string) $permitfact['permitfact_permit_date_verbose']);
            $spreadsheet->getActiveSheet()->setCellValue("E" . $row, (string) $permitfact['permitfact_project_address']);
            $spreadsheet->getActiveSheet()->setCellValue("F" . $row, (string) $permitfact['permitfact_project_name']);
            $spreadsheet->getActiveSheet()->setCellValue("G" . $row, (string) $permitfact['permitfact_project_type_verbose']);
            $spreadsheet->getActiveSheet()->setCellValue("H" . $row, (string) $permitfact['project_city_verbose']);
            $spreadsheet->getActiveSheet()->setCellValue("I" . $row, (string) $permitfact['permitfact_project_state']);
            $spreadsheet->getActiveSheet()->setCellValue("J" . $row, (string) $permitfact['permitfact_project_zip']);
            $spreadsheet->getActiveSheet()->setCellValue("K" . $row, (string) $permitfact['permitfact_company']);
            $spreadsheet->getActiveSheet()->setCellValue("L" . $row, (string) $permitfact['permitfact_address']);
            $spreadsheet->getActiveSheet()->setCellValue("M" . $row, (string) $permitfact['permitfact_city_verbose']);
            $spreadsheet->getActiveSheet()->setCellValue("N" . $row, (string) $permitfact['permitfact_state']);
            $spreadsheet->getActiveSheet()->setCellValue("O" . $row, (string) $permitfact['permitfact_zip']);
            $spreadsheet->getActiveSheet()->setCellValue("P" . $row, $this->formatPhoneNumber((string) $permitfact['permitfact_phone']));
            $spreadsheet->getActiveSheet()->setCellValue("Q" . $row, (string) $permitfact['permitfact_size']);
            $spreadsheet->getActiveSheet()->setCellValue("R" . $row, (string) $permitfact['permitfact_value']);
            $spreadsheet->getActiveSheet()->setCellValue("S" . $row, (string) $permitfact['permitfact_work_type_verbose']);
            $spreadsheet->getActiveSheet()->setCellValue("T" . $row, (string) $permitfact['permitfact_building_type_verbose']);
            $spreadsheet->getActiveSheet()->setCellValue("U" . $row, (string) $permitfact['permitfact_county_name_verbose']);
            $spreadsheet->getActiveSheet()->setCellValue("V" . $row, (string) $permitfact['permitfact_owner_name']);
            $spreadsheet->getActiveSheet()->setCellValue("W" . $row, (string) $permitfact['permitfact_owner_address']);
            $spreadsheet->getActiveSheet()->setCellValue("X" . $row, (string) $permitfact['permitfact_owner_city']);
            $spreadsheet->getActiveSheet()->setCellValue("Y" . $row, (string) $permitfact['permitfact_owner_state']);
            $spreadsheet->getActiveSheet()->setCellValue("Z" . $row, (string) $permitfact['permitfact_owner_zip']);
            $spreadsheet->getActiveSheet()->setCellValue("AA" . $row, $this->formatPhoneNumber((string) $permitfact['permitfact_owner_phone']));

            $row++;
        }

        $spreadsheet->setActiveSheetIndex(0);

        $excelWriter = new Xlsx($spreadsheet);

        $absPath = __DIR__ . "/../{$exportjob_base_directory}/{$exportjob_file_name}";

        $excelWriter->save($absPath);

        return $absPath;
    }

    // ============================
    // Creates Business Facts Excel
    // ============================

    private function createExcelExport_BusinessFacts($businessfacts, $exportjob_base_directory, $exportjob_file_name) {

        // Create the Spreadsheet.

        $spreadsheet = new Spreadsheet();

        $spreadsheet->getProperties()->setCreator("DEC International")->setTitle("BusinessFacts")->setSubject("BusinessFacts")
                ->setDescription("BusinessFacts")->setKeywords("BusinessFacts")->setCategory("BusinessFacts");

        // Create the Base Sheet.

        $spreadsheet->setActiveSheetIndex(0);
        $spreadsheet->getActiveSheet()->setTitle("BusinessFacts");

        // Format the Cells and Create the Headings.

        $spreadsheet->getActiveSheet()->getColumnDimension("A")->setAutoSize(true);
        $spreadsheet->getActiveSheet()->getColumnDimension("B")->setAutoSize(true);
        $spreadsheet->getActiveSheet()->getColumnDimension("C")->setAutoSize(true);
        $spreadsheet->getActiveSheet()->getColumnDimension("D")->setAutoSize(true);
        $spreadsheet->getActiveSheet()->getColumnDimension("E")->setAutoSize(true);
        $spreadsheet->getActiveSheet()->getColumnDimension("F")->setAutoSize(true);
        $spreadsheet->getActiveSheet()->getColumnDimension("G")->setAutoSize(true);
        $spreadsheet->getActiveSheet()->getColumnDimension("H")->setAutoSize(true);
        $spreadsheet->getActiveSheet()->getColumnDimension("I")->setAutoSize(true);
        $spreadsheet->getActiveSheet()->getColumnDimension("J")->setAutoSize(true);
        $spreadsheet->getActiveSheet()->getColumnDimension("K")->setAutoSize(true);
        $spreadsheet->getActiveSheet()->getColumnDimension("L")->setAutoSize(true);
        $spreadsheet->getActiveSheet()->getColumnDimension("M")->setAutoSize(true);
        $spreadsheet->getActiveSheet()->getColumnDimension("N")->setAutoSize(true);
        $spreadsheet->getActiveSheet()->getColumnDimension("O")->setAutoSize(true);
        $spreadsheet->getActiveSheet()->getColumnDimension("P")->setAutoSize(true);
        $spreadsheet->getActiveSheet()->getColumnDimension("Q")->setAutoSize(true);
        $spreadsheet->getActiveSheet()->getColumnDimension("R")->setAutoSize(true);
        $spreadsheet->getActiveSheet()->getColumnDimension("S")->setAutoSize(true);
        $spreadsheet->getActiveSheet()->getColumnDimension("T")->setAutoSize(true);
        $spreadsheet->getActiveSheet()->getColumnDimension("U")->setAutoSize(true);
        $spreadsheet->getActiveSheet()->getColumnDimension("V")->setAutoSize(true);
        $spreadsheet->getActiveSheet()->getColumnDimension("W")->setAutoSize(true);

        $spreadsheet->getActiveSheet()->getStyle("A1:W1")->getFont()->setBold(true);
        $spreadsheet->getActiveSheet()->getStyle("A1:W1")->getFill()->setFillType(\PhpOffice\PhpSpreadsheet\Style\Fill::FILL_SOLID)->getStartColor()->setARGB("D3D3D3");

        $spreadsheet->setActiveSheetIndex(0)->setCellValue("A1", "BusinessFactID")
                ->setCellValue("B1", "Company")
                ->setCellValue("C1", "Address1")
                ->setCellValue("D1", "Address2")
                ->setCellValue("E1", "City")
                ->setCellValue("F1", "State")
                ->setCellValue("G1", "Zip")
                ->setCellValue("H1", "County")
                ->setCellValue("I1", "Phone")
                ->setCellValue("J1", "Fax")
                ->setCellValue("K1", "EmailAddress")
                ->setCellValue("L1", "BC")
                ->setCellValue("M1", "Description")
                ->setCellValue("N1", "ES")
                ->setCellValue("O1", "BusinessInception")
                ->setCellValue("P1", "SIC")
                ->setCellValue("Q1", "SIC_Description")
                ->setCellValue("R1", "ContactName")
                ->setCellValue("S1", "ContactTitle")
                ->setCellValue("T1", "LicenseDate")
                ->setCellValue("U1", "HomeBasedBusiness")
                ->setCellValue("V1", "YearEstablished")
                ->setCellValue("W1", "EntryDate");

        $spreadsheet->getActiveSheet()->getStyle("A:A")->getNumberFormat()->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_NUMBER);
        $spreadsheet->getActiveSheet()->getStyle("C:C")->getNumberFormat()->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_TEXT);
        $spreadsheet->getActiveSheet()->getStyle("D:D")->getNumberFormat()->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_TEXT);
        $spreadsheet->getActiveSheet()->getStyle("E:E")->getNumberFormat()->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_TEXT);
        $spreadsheet->getActiveSheet()->getStyle("F:F")->getNumberFormat()->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_TEXT);
        $spreadsheet->getActiveSheet()->getStyle("G:G")->getNumberFormat()->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_TEXT);
        $spreadsheet->getActiveSheet()->getStyle("H:H")->getNumberFormat()->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_TEXT);
        $spreadsheet->getActiveSheet()->getStyle("I:I")->getNumberFormat()->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_TEXT);
        $spreadsheet->getActiveSheet()->getStyle("J:J")->getNumberFormat()->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_TEXT);
        $spreadsheet->getActiveSheet()->getStyle("K:K")->getNumberFormat()->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_TEXT);
        $spreadsheet->getActiveSheet()->getStyle("L:L")->getNumberFormat()->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_TEXT);
        $spreadsheet->getActiveSheet()->getStyle("M:M")->getNumberFormat()->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_TEXT);
        $spreadsheet->getActiveSheet()->getStyle("N:N")->getNumberFormat()->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_TEXT);
        $spreadsheet->getActiveSheet()->getStyle("O:O")->getNumberFormat()->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_TEXT);
        $spreadsheet->getActiveSheet()->getStyle("P:P")->getNumberFormat()->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_TEXT);
        $spreadsheet->getActiveSheet()->getStyle("Q:Q")->getNumberFormat()->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_TEXT);
        $spreadsheet->getActiveSheet()->getStyle("R:R")->getNumberFormat()->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_TEXT);
        $spreadsheet->getActiveSheet()->getStyle("S:S")->getNumberFormat()->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_TEXT);
        $spreadsheet->getActiveSheet()->getStyle("T:T")->getNumberFormat()->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_TEXT);
        $spreadsheet->getActiveSheet()->getStyle("U:U")->getNumberFormat()->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_TEXT);
        $spreadsheet->getActiveSheet()->getStyle("V:V")->getNumberFormat()->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_TEXT);
        $spreadsheet->getActiveSheet()->getStyle("W:W")->getNumberFormat()->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_TEXT);

        $spreadsheet->getActiveSheet()->freezePane("A2");

        // Create the Detail Rows.

        $row = 2;

        foreach ($businessfacts as $businessfact) {

            $spreadsheet->getActiveSheet()->setCellValue("A" . $row, (string) $businessfact['businessfact_legacy_id']);
            $spreadsheet->getActiveSheet()->setCellValue("B" . $row, (string) $businessfact['businessfact_company']);
            $spreadsheet->getActiveSheet()->setCellValue("C" . $row, (string) $businessfact['businessfact_address_one']);
            $spreadsheet->getActiveSheet()->setCellValue("D" . $row, (string) $businessfact['businessfact_address_two']);
            $spreadsheet->getActiveSheet()->setCellValue("E" . $row, (string) $businessfact['businessfact_city_verbose']);
            $spreadsheet->getActiveSheet()->setCellValue("F" . $row, (string) $businessfact['businessfact_state']);
            $spreadsheet->getActiveSheet()->setCellValue("G" . $row, (string) $businessfact['businessfact_zip']);
            $spreadsheet->getActiveSheet()->setCellValue("H" . $row, (string) $businessfact['businessfact_county_name_verbose']);
            $spreadsheet->getActiveSheet()->setCellValue("I" . $row, (string) $this->formatPhoneNumber($businessfact['businessfact_phone']));
            $spreadsheet->getActiveSheet()->setCellValue("J" . $row, (string) $this->formatPhoneNumber($businessfact['businessfact_fax']));
            $spreadsheet->getActiveSheet()->setCellValue("K" . $row, (string) $businessfact['businessfact_email_address']);
            $spreadsheet->getActiveSheet()->setCellValue("L" . $row, (string) $businessfact['businessfact_business_class_verbose']);
            $spreadsheet->getActiveSheet()->setCellValue("M" . $row, (string) $businessfact['businessfact_action_verbose']);
            $spreadsheet->getActiveSheet()->setCellValue("N" . $row, (string) $businessfact['businessfact_employee_count']);
            $spreadsheet->getActiveSheet()->setCellValue("O" . $row, (string) $businessfact['businessfact_inception']);
            $spreadsheet->getActiveSheet()->setCellValue("P" . $row, (string) $businessfact['businessfact_sic_code']);
            $spreadsheet->getActiveSheet()->setCellValue("Q" . $row, (string) $businessfact['businessfact_siccode_description_verbose']);
            $spreadsheet->getActiveSheet()->setCellValue("R" . $row, (string) $businessfact['businessfact_contact_name']);
            $spreadsheet->getActiveSheet()->setCellValue("S" . $row, (string) $businessfact['businessfact_contact_title']);
            $spreadsheet->getActiveSheet()->setCellValue("T" . $row, (string) $businessfact['businessfact_license_date_verbose']);
            $spreadsheet->getActiveSheet()->setCellValue("U" . $row, (string) $businessfact['businessfact_home_based_business']);
            $spreadsheet->getActiveSheet()->setCellValue("V" . $row, (string) $businessfact['businessfact_year_established']);
            $spreadsheet->getActiveSheet()->setCellValue("W" . $row, (string) $businessfact['businessfact_entry_date_verbose']);

            $row++;
        }

        $spreadsheet->setActiveSheetIndex(0);

        $excelWriter = new Xlsx($spreadsheet);

        $absPath = __DIR__ . "/../{$exportjob_base_directory}/{$exportjob_file_name}";

        $excelWriter->save($absPath);

        return $absPath;
    }

    // ======================
    // Creates PZ Facts Excel
    // ======================

    private function createExcelExport_PZFacts($pzfacts, $exportjob_base_directory, $exportjob_file_name) {

        // Create the Spreadsheet

        $spreadsheet = new Spreadsheet();

        $spreadsheet->getProperties()->setCreator("DEC International")->setTitle("PlanningAndZoningFacts")->setSubject("PlanningAndZoningFacts")
                ->setDescription("PlanningAndZoningFacts")->setKeywords("PlanningAndZoningFacts")->setCategory("PlanningAndZoningFacts");

        // Create the Base Sheet.

        $spreadsheet->setActiveSheetIndex(0);
        $spreadsheet->getActiveSheet()->setTitle("PlanningAndZoningFacts");

        // Format the Cells and Create the Headings.

        $spreadsheet->getActiveSheet()->getColumnDimension("A")->setAutoSize(true);
        $spreadsheet->getActiveSheet()->getColumnDimension("B")->setAutoSize(true);
        $spreadsheet->getActiveSheet()->getColumnDimension("C")->setAutoSize(true);
        $spreadsheet->getActiveSheet()->getColumnDimension("D")->setAutoSize(true);
        $spreadsheet->getActiveSheet()->getColumnDimension("E")->setAutoSize(true);
        $spreadsheet->getActiveSheet()->getColumnDimension("F")->setAutoSize(true);
        $spreadsheet->getActiveSheet()->getColumnDimension("G")->setAutoSize(true);
        $spreadsheet->getActiveSheet()->getColumnDimension("H")->setAutoSize(true);
        $spreadsheet->getActiveSheet()->getColumnDimension("I")->setAutoSize(true);
        $spreadsheet->getActiveSheet()->getColumnDimension("J")->setAutoSize(true);
        $spreadsheet->getActiveSheet()->getColumnDimension("K")->setAutoSize(true);
        $spreadsheet->getActiveSheet()->getColumnDimension("L")->setAutoSize(true);
        $spreadsheet->getActiveSheet()->getColumnDimension("M")->setAutoSize(true);
        $spreadsheet->getActiveSheet()->getColumnDimension("N")->setAutoSize(true);
        $spreadsheet->getActiveSheet()->getColumnDimension("O")->setAutoSize(true);
        $spreadsheet->getActiveSheet()->getColumnDimension("P")->setAutoSize(true);
        $spreadsheet->getActiveSheet()->getColumnDimension("Q")->setAutoSize(true);
        $spreadsheet->getActiveSheet()->getColumnDimension("R")->setAutoSize(true);
        $spreadsheet->getActiveSheet()->getColumnDimension("S")->setAutoSize(true);
        $spreadsheet->getActiveSheet()->getColumnDimension("T")->setAutoSize(true);
        $spreadsheet->getActiveSheet()->getColumnDimension("U")->setAutoSize(true);
        $spreadsheet->getActiveSheet()->getColumnDimension("V")->setAutoSize(true);
        $spreadsheet->getActiveSheet()->getColumnDimension("W")->setAutoSize(true);
        $spreadsheet->getActiveSheet()->getColumnDimension("X")->setAutoSize(true);
        $spreadsheet->getActiveSheet()->getColumnDimension("Y")->setAutoSize(true);
        $spreadsheet->getActiveSheet()->getColumnDimension("Z")->setAutoSize(true);
        $spreadsheet->getActiveSheet()->getColumnDimension("AA")->setAutoSize(true);

        $spreadsheet->getActiveSheet()->getStyle("A1:AA1")->getFont()->setBold(true);
        $spreadsheet->getActiveSheet()->getStyle("A1:AA1")->getFill()->setFillType(\PhpOffice\PhpSpreadsheet\Style\Fill::FILL_SOLID)->getStartColor()->setARGB("D3D3D3");

        $spreadsheet->setActiveSheetIndex(0)->setCellValue("A1", "PZ_ID")
                ->setCellValue("B1", "County")
                ->setCellValue("C1", "OwnerName")
                ->setCellValue("D1", "OwnerState")
                ->setCellValue("E1", "OwnerAddress")
                ->setCellValue("F1", "OwnerPhone")
                ->setCellValue("G1", "OwnerFax")
                ->setCellValue("H1", "ProfessionalName")
                ->setCellValue("I1", "ProfessionalState")
                ->setCellValue("J1", "ProfessionalAddress")
                ->setCellValue("K1", "ProfessionalPhone")
                ->setCellValue("L1", "ProfessionalFax")
                ->setCellValue("M1", "ProjectType")
                ->setCellValue("N1", "ProjectName")
                ->setCellValue("O1", "ProjectDescription")
                ->setCellValue("P1", "ProjectAddress")
                ->setCellValue("Q1", "ProjectCity")
                ->setCellValue("R1", "ActionCode")
                ->setCellValue("S1", "District")
                ->setCellValue("T1", "LandLot")
                ->setCellValue("U1", "Block")
                ->setCellValue("V1", "Parcel")
                ->setCellValue("W1", "Lot")
                ->setCellValue("X1", "ActionDate")
                ->setCellValue("Y1", "Acres")
                ->setCellValue("Z1", "Status")
                ->setCellValue("AA1", "EntryDate");

        $spreadsheet->getActiveSheet()->getStyle("A:A")->getNumberFormat()->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_NUMBER);
        $spreadsheet->getActiveSheet()->getStyle("C:C")->getNumberFormat()->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_TEXT);
        $spreadsheet->getActiveSheet()->getStyle("D:D")->getNumberFormat()->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_TEXT);
        $spreadsheet->getActiveSheet()->getStyle("E:E")->getNumberFormat()->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_TEXT);
        $spreadsheet->getActiveSheet()->getStyle("F:F")->getNumberFormat()->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_TEXT);
        $spreadsheet->getActiveSheet()->getStyle("G:G")->getNumberFormat()->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_TEXT);
        $spreadsheet->getActiveSheet()->getStyle("H:H")->getNumberFormat()->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_TEXT);
        $spreadsheet->getActiveSheet()->getStyle("I:I")->getNumberFormat()->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_TEXT);
        $spreadsheet->getActiveSheet()->getStyle("J:J")->getNumberFormat()->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_TEXT);
        $spreadsheet->getActiveSheet()->getStyle("K:K")->getNumberFormat()->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_TEXT);
        $spreadsheet->getActiveSheet()->getStyle("L:L")->getNumberFormat()->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_TEXT);
        $spreadsheet->getActiveSheet()->getStyle("M:M")->getNumberFormat()->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_TEXT);
        $spreadsheet->getActiveSheet()->getStyle("N:N")->getNumberFormat()->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_TEXT);
        $spreadsheet->getActiveSheet()->getStyle("O:O")->getNumberFormat()->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_TEXT);
        $spreadsheet->getActiveSheet()->getStyle("P:P")->getNumberFormat()->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_TEXT);
        $spreadsheet->getActiveSheet()->getStyle("Q:Q")->getNumberFormat()->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_TEXT);
        $spreadsheet->getActiveSheet()->getStyle("R:R")->getNumberFormat()->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_TEXT);
        $spreadsheet->getActiveSheet()->getStyle("S:S")->getNumberFormat()->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_TEXT);
        $spreadsheet->getActiveSheet()->getStyle("T:T")->getNumberFormat()->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_TEXT);
        $spreadsheet->getActiveSheet()->getStyle("U:U")->getNumberFormat()->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_TEXT);
        $spreadsheet->getActiveSheet()->getStyle("V:V")->getNumberFormat()->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_TEXT);
        $spreadsheet->getActiveSheet()->getStyle("W:W")->getNumberFormat()->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_TEXT);
        $spreadsheet->getActiveSheet()->getStyle("X:X")->getNumberFormat()->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_TEXT);
        $spreadsheet->getActiveSheet()->getStyle("Y:Y")->getNumberFormat()->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_TEXT);
        $spreadsheet->getActiveSheet()->getStyle("Z:Z")->getNumberFormat()->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_TEXT);
        $spreadsheet->getActiveSheet()->getStyle("AA:AA")->getNumberFormat()->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_TEXT);

        $spreadsheet->getActiveSheet()->freezePane("A2");

        // Create the Detail Rows.

        $row = 2;

        foreach ($pzfacts as $pzfact) {

            $spreadsheet->getActiveSheet()->setCellValue("A" . $row, (string) $pzfact['pzfact_legacy_id']);
            $spreadsheet->getActiveSheet()->setCellValue("B" . $row, (string) $pzfact['pzfact_county_name_verbose']);
            $spreadsheet->getActiveSheet()->setCellValue("C" . $row, (string) $pzfact['pzfact_owner_name']);
            $spreadsheet->getActiveSheet()->setCellValue("D" . $row, (string) $pzfact['pzfact_owner_state']);
            $spreadsheet->getActiveSheet()->setCellValue("E" . $row, (string) $pzfact['pzfact_owner_address']);
            $spreadsheet->getActiveSheet()->setCellValue("F" . $row, $this->formatPhoneNumber((string) $pzfact['pzfact_owner_phone']));
            $spreadsheet->getActiveSheet()->setCellValue("G" . $row, $this->formatPhoneNumber((string) $pzfact['pzfact_owner_fax']));
            $spreadsheet->getActiveSheet()->setCellValue("H" . $row, (string) $pzfact['pzfact_professional_name']);
            $spreadsheet->getActiveSheet()->setCellValue("I" . $row, (string) $pzfact['pzfact_professional_state']);
            $spreadsheet->getActiveSheet()->setCellValue("J" . $row, (string) $pzfact['pzfact_professional_address']);
            $spreadsheet->getActiveSheet()->setCellValue("K" . $row, $this->formatPhoneNumber((string) $pzfact['pzfact_professional_phone']));
            $spreadsheet->getActiveSheet()->setCellValue("L" . $row, $this->formatPhoneNumber((string) $pzfact['pzfact_professional_fax']));
            $spreadsheet->getActiveSheet()->setCellValue("M" . $row, (string) $pzfact['pzfact_project_type_verbose']);
            $spreadsheet->getActiveSheet()->setCellValue("N" . $row, (string) $pzfact['pzfact_project_name']);
            $spreadsheet->getActiveSheet()->setCellValue("O" . $row, (string) $pzfact['pzfact_project_description']);
            $spreadsheet->getActiveSheet()->setCellValue("P" . $row, (string) $pzfact['pzfact_project_address']);
            $spreadsheet->getActiveSheet()->setCellValue("Q" . $row, (string) $pzfact['project_city_verbose']);
            $spreadsheet->getActiveSheet()->setCellValue("R" . $row, (string) $pzfact['pzfact_action_code_verbose']);
            $spreadsheet->getActiveSheet()->setCellValue("S" . $row, (string) $pzfact['pzfact_district']);
            $spreadsheet->getActiveSheet()->setCellValue("T" . $row, (string) $pzfact['pzfact_land_lot']);
            $spreadsheet->getActiveSheet()->setCellValue("U" . $row, (string) $pzfact['pzfact_block']);
            $spreadsheet->getActiveSheet()->setCellValue("V" . $row, (string) $pzfact['pzfact_parcel_number']);
            $spreadsheet->getActiveSheet()->setCellValue("W" . $row, (string) $pzfact['pzfact_lot']);
            $spreadsheet->getActiveSheet()->setCellValue("X" . $row, (string) $pzfact['pzfact_action_date_verbose']);
            $spreadsheet->getActiveSheet()->setCellValue("Y" . $row, (string) $pzfact['pzfact_acres']);
            $spreadsheet->getActiveSheet()->setCellValue("Z" . $row, (string) $pzfact['pzfact_status']);
            $spreadsheet->getActiveSheet()->setCellValue("AA" . $row, (string) $pzfact['pzfact_entry_date_verbose']);

            $row++;
        }

        $spreadsheet->setActiveSheetIndex(0);

        $excelWriter = new Xlsx($spreadsheet);

        $absPath = __DIR__ . "/../{$exportjob_base_directory}/{$exportjob_file_name}";

        $excelWriter->save($absPath);

        return $absPath;
    }

    // =======================
    // Format Raw Phone Number
    // =======================

    public function formatPhoneNumber(string $phonenumber): string {

        // Remove everything except digits
        $digits = preg_replace('/\D+/', '', $phonenumber);

        // Handle leading country code (US)
        if (strlen($digits) === 11 && str_starts_with($digits, '1')) {
            $digits = substr($digits, 1);
        }

        return substr($digits, 0, 3) . '-' . substr($digits, 3, 3) . '-' . substr($digits, 6, 4);
    }

    // ========================
    // Creates Permit Facts PDF
    // ========================

    private function createPDFExport_PermitFacts($permitfacts, $usersubscription, $exportjob_base_directory, $exportjob_file_name) {

        require_once ("./reports/Print_AllPermitFactsPDF.php");

        $report = (new Print_PermitFactsPDF())->print($permitfacts, $usersubscription, $exportjob_base_directory, $exportjob_file_name);
        return $report;
    }

    // ===========================
    // Creates Planning/Zoning PDF
    // ===========================

    private function createPDFExport_PZFacts($pzfacts, $usersubscription, $exportjob_base_directory, $exportjob_file_name) {

        require_once ("./reports/Print_AllPZFactsPDF.php");

        $report = (new Print_PZFactsPDF())->print($pzfacts, $usersubscription, $exportjob_base_directory, $exportjob_file_name);
        return $report;
    }

    // ========================
    // Creates Permit Facts PDF
    // ========================

    private function createPDFExport_BusinessFacts($businessfacts, $usersubscription, $exportjob_base_directory, $exportjob_file_name) {

        require_once ("./reports/Print_AllBusinessFactsPDF.php");

        $report = (new Print_BusinessFactsPDF())->print($businessfacts, $usersubscription, $exportjob_base_directory, $exportjob_file_name);
        return $report;
    }

    // ======================
    // Return Unique Job Code
    // ======================

    private function getUniqueJobCode() {
        return sprintf('%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
                random_int(0, 0xffff), random_int(0, 0xffff), random_int(0, 0xffff),
                random_int(0, 0x0fff) | 0x4000, random_int(0, 0x3fff) | 0x8000,
                random_int(0, 0xffff), random_int(0, 0xffff), random_int(0, 0xffff)
        );
    }

    // ==========================
    // Send an Alert/Notification
    // ==========================

    public function sendWebHookAlert($message, $title = "🚨 DEC Application Alert", $alerttype = "1fff00") {

        $webhookUrl = (string) $this->settings->DiscordWebHookURL;

        $data = [
            'embeds' => [[
            'title' => $title,
            'description' => $message,
            'color' => hexdec($alerttype),
            'footer' => [
                'text' => 'Real Estate Data Inc. System Notificiation',
            ],
            'timestamp' => date('c'), // ISO 8601 format
                ]]
        ];

        $jsonData = json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);

        $ch = curl_init($webhookUrl);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $jsonData);
        curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);

        $response = curl_exec($ch);

        if ($response === false) {
            $this->log('Curl error: ' . curl_error($ch));
        }

        curl_close($ch);
    }

    // ============
    // Email Admins
    // ============

    public function email_admins($subject = "", $message = "") {

        $this->log(date("Y-m-d H:i:s") . " - Emailing Admins...");

        $settings = new SimpleXMLElement((file_exists("./xml/settings.xml") ? file_get_contents("./xml/settings.xml") : "<settings/>"));

        // Server Variables
        $host = (string) $settings->SmtpServer;
        $username = (string) $settings->SmtpUserName;
        $password = (string) $settings->SmtpPassword;

        // Recipient Variables
        $from_email = (string) $settings->FromEmail;
        $from_name = (string) $settings->FromName;

        $support_email = (string) $settings->SupportEmail;
        $support_name = (string) $settings->SupportName;

        if ((string) $settings->Environment == 'Production') {

            $admin_email = (string) $settings->AdminEmail;
            $admin_name = (string) $settings->AdminName;
        }

        // Content Variables

        $attachment_name = "";

        $mail = new PHPMailer(true);

        try {

            //Server settings
            $mail->isSMTP();
            $mail->Host = $host;
            $mail->SMTPAuth = true;
            $mail->Username = $username;
            $mail->Password = $password;
            $mail->SMTPSecure = PHPMailer::ENCRYPTION_SMTPS;
            $mail->Port = 465;

            //Recipients
            $mail->setFrom($from_email, $from_name);
            $mail->addAddress($support_email, $support_name);

            if ($settings->Environment == 'Production') {
                $mail->addAddress($admin_email, $admin_name);
            }

            //Attachments
            $mail->addAttachment($attachment_name);

            //Content
            $mail->isHTML(true);
            $mail->Subject = $subject;
            $mail->Body = $message;

            $mail->send();
        } catch (Exception $e) {
            $this->log("Message could not be sent. Mailer Error: {$e->ErrorInfo}");
        }
    }
}

?>