<?php

namespace classes;

use GuzzleHttp\Client;
use interfaces\notification_i;
use objects\departments_o;
use objects\users_o;
use slack\slack_c;
use traits\notification_t;

require_once WD . '/modules/slack/slack_c.php';

class slack implements notification_i
{
    use notification_t;

    private ?slack_c $config = null;

    public function getConfig(): slack_c
    {
        if ($this->config === null) {
            $this->config = new slack_c();
        }

        return $this->config;
    }

    /**
     * @inheritdoc
     * @throws \Exception
     */
    public function send_department_booking_notification(int $department_id, $message): self
    {
        // Get the departments webhook
        $webhook = self::get_department_webhook($department_id);
        // Check if the webhook is empty
        if (empty($webhook)) {
            throw new \Exception('Department webhook is empty');
        }
        // Send the notification to the department
        self::add_log(self::send_webhook_message($message, $webhook));
        return $this;
    }

    private function get_department_webhook(int $department_id): string|null
    {
        // Check if the department webhook is cached
        $webhook = redis->get_department_webhook($department_id);
        // If the department webhook is not cached, get it from the database
        if ($webhook === null) {
            $department = (new departments_o())->select((int)$department_id);
            $webhook = $department->slack_webhook->value();
            // Cache the department webhook (If it is not empty or null)
            if (!empty($webhook)) {
                redis->cache_department_webhook($department_id, $webhook);
            }
        }
        return redis->get_department_webhook($department_id);
    }

    /**
     * Send a message to a slack webhook
     * @param string $message
     * @param string $webhook
     * @return string The response from the webhook, unparsed
     */
    public function send_webhook_message(string $message, string $webhook): string
    {
        global $DEBUG;
        try {
            // Initialize Guzzle client
            $client = new Client();

            // Prepare the payload for the webhook
            $payload = [
                'text' => $message
            ];

            // Send POST request to the webhook
            $response = $client->post($webhook, [
                'headers' => ['Content-Type' => 'application/json'],
                'body' => json_encode($payload),
                'verify' => ($DEBUG === false) // Disable SSL verification (Only in development)
            ]);

            // Return success message
            return 'Message sent successfully. Response: ' . $response->getBody();
        } catch (\Exception $e) {
            // Handle any errors that occur
            return 'Failed to send message: ' . $e->getMessage();
        }
    }

    public function format_new_booking($id, $customer_number, string $wash_type, string $contact_email, string $reference_number, string $regNrTraekker, string $regNrTrailer, string $washCertificateEmail, string $date, int $department, $pickup_bool, string $notes, string $washCertificateStatus, string $washCertificateUrl, string $status): string
    {
        // Get the department name
        $department_name = (new departments_o())->getDepartmentName($department);
        // Get the customer name
        $customer_name = (new users_o())->getCustomerName($customer_number);
        // Format the message
        return "*Ny booking oprettet* ( ID: " . $id . " )\n"
            . "Kunde: $customer_name ($customer_number)\n"
            . "Type: $wash_type\n"
            . "Reference nummer: $reference_number\n"
            . "RegNr Traekker: $regNrTraekker\n"
            . "RegNr Trailer: $regNrTrailer\n"
            . "Dato: $date\n"
            . "Hentning: $pickup_bool\n"
            . "Noter: $notes";
    }

    public function format_unfulfilled_booking($id, $customer_number, string $wash_type, string $contact_email, string $reference_number, string $regNrTraekker, string $regNrTrailer, string $washCertificateEmail, string $date, int $department, $pickup_bool, string $notes, string $washCertificateStatus, string $washCertificateUrl, string $status): string
    {
        // Get the department name
        $department_name = (new departments_o())->getDepartmentName($department);
        // Get the customer name
        $customer_name = (new users_o())->getCustomerName($customer_number);
        // Format the message
        return "Unfulfilled booking from " . $date . "\n"
            . "ID: $id\n"
            . "Customer: $customer_name\n"
            . "Customer number: $customer_number\n"
            . "Wash type: $wash_type\n"
            . "Contact email: $contact_email\n"
            . "Reference number: $reference_number\n"
            . "RegNr Traekker: $regNrTraekker\n"
            . "RegNr Trailer: $regNrTrailer\n"
            . "Wash certificate email: $washCertificateEmail\n"
            . "Date: $date\n"
            . "Department: $department_name\n"
            . "Pickup: $pickup_bool\n"
            . "Notes: $notes\n"
            . "Status: $status";
    }

    public function send_message(string $string, ?string $module = null): void
    {
        global $SLACK_DEFAULT_WEBHOOK;
        // Format the message if a module is provided
        if ($module !== null) {
            $string = "*$module*\n" . $string;
        }
        // Send the message to the slack webhook
        self::add_log(self::send_webhook_message($string, $SLACK_DEFAULT_WEBHOOK));
    }

    public function send_customer_registration_notification(int $customer_number): self
    {
        $webhook = $this->get_customer_registration_webhook_url();
        if ($webhook === '') {
            return $this;
        }

        self::add_log(self::send_webhook_message(
            $this->format_customer_registration($customer_number),
            $webhook
        ));

        return $this;
    }

    /**
     * Send a sanitized customer-registration test notification to the saved Slack webhook.
     *
     * @return array{configured:bool,sent:bool,message:string}
     */
    public function test_customer_registration_webhook(): array
    {
        $webhook = $this->get_customer_registration_webhook_url();
        if ($webhook === '') {
            return [
                'configured' => false,
                'sent' => false,
                'message' => 'Slack customer registration webhook URL is not configured.',
            ];
        }

        $result = $this->send_webhook_message(
            $this->format_customer_registration_test(),
            $webhook
        );
        $sent = $this->is_webhook_send_successful($result);

        self::add_log($sent
            ? 'Slack customer registration test webhook sent successfully.'
            : 'Slack customer registration test webhook failed.'
        );

        return [
            'configured' => true,
            'sent' => $sent,
            'message' => $sent
                ? 'Slack test message sent successfully.'
                : 'Slack test message failed.',
        ];
    }

    /**
     * Send a sanitized internal department goal progress test notification to the saved Slack webhook.
     *
     * @return array{configured:bool,sent:bool,message:string}
     */
    public function test_internal_department_goal_progress_webhook(): array
    {
        $webhook = $this->get_internal_department_goal_progress_webhook_url();
        if ($webhook === '') {
            return [
                'configured' => false,
                'sent' => false,
                'message' => 'Slack internal department goal progress webhook URL is not configured.',
            ];
        }

        $result = $this->send_webhook_message(
            $this->format_internal_department_goal_progress_test(),
            $webhook
        );
        $sent = $this->is_webhook_send_successful($result);

        self::add_log($sent
            ? 'Slack internal department goal progress test webhook sent successfully.'
            : 'Slack internal department goal progress test webhook failed.'
        );

        return [
            'configured' => true,
            'sent' => $sent,
            'message' => $sent
                ? 'Slack test message sent successfully.'
                : 'Slack test message failed.',
        ];
    }

    protected function get_customer_registration_webhook_url(): string
    {
        return trim((string)$this->getConfig()->customer_registration_webhook_url->getVariableValue());
    }

    public function get_internal_department_goal_progress_webhook_url(): string
    {
        return trim((string)$this->getConfig()->internal_department_goal_progress_webhook_url->getVariableValue());
    }

    /**
     * @return int[]
     */
    public function get_internal_department_ids(): array
    {
        return $this->getConfig()->internal_department_ids->getDepartmentIds();
    }

    /**
     * @param int[] $department_ids
     * @throws \Exception
     */
    public function set_internal_department_goal_progress_config(string $webhook_url, array $department_ids): array
    {
        $this->getConfig()->internal_department_goal_progress_webhook_url->setVariableValue(trim($webhook_url));
        $this->getConfig()->internal_department_ids->setVariableValue($department_ids);

        return $this->get_internal_department_goal_progress_config();
    }

    public function get_internal_department_goal_progress_config(): array
    {
        $departments = (new departments_o())->getFieldsWhere(
            [
                'visible' => 1,
                'archived' => 0,
            ],
            [
                'id',
                'name',
                'order_priority',
            ]
        );

        usort($departments, static function (array $a, array $b): int {
            return (int)($a['order_priority'] ?? 0) <=> (int)($b['order_priority'] ?? 0)
                ?: (int)($a['id'] ?? 0) <=> (int)($b['id'] ?? 0);
        });

        return [
            'internal_department_goal_progress_webhook_url' => $this->get_internal_department_goal_progress_webhook_url(),
            'internal_department_ids' => $this->get_internal_department_ids(),
            'departments' => array_map(static function (array $department): array {
                return [
                    'id' => (int)$department['id'],
                    'name' => (string)$department['name'],
                    'order_priority' => (int)$department['order_priority'],
                ];
            }, $departments),
        ];
    }

    public function is_webhook_send_successful(string $result): bool
    {
        return !str_starts_with($result, 'Failed to send message:');
    }

    public function format_customer_registration(int $customer_number): string
    {
        $customer = (new users_o())->getUserByCustomerNumber($customer_number);
        $customerName = $customer->exists()
            ? $customer->getCustomerName((int)$customer->customer_number->value())
            : '';
        $customerName = trim((string)$customerName);
        if ($customerName === '') {
            $customerName = 'Unknown customer';
        }

        $safeCustomerNumber = (int)$customer_number;
        $customerUrl = 'https://truckwash.io/superuser/users?search=' . $safeCustomerNumber;

        return "*New customer registered on Truck Wash*\n"
            . "Customer: $customerName ($safeCustomerNumber)\n"
            . "Open in Superuser: $customerUrl";
    }

    public function format_customer_registration_test(): string
    {
        return "*Truck Wash Slack test*\n"
            . "Customer registration notifications are configured correctly.";
    }

    public function format_internal_department_goal_progress_test(): string
    {
        return "*Truck Wash Slack test*\n"
            . "Internal department goal progress notifications are configured correctly.";
    }
}
