<?php

namespace routes;

use classes\authentication;
use classes\response;
use classes\email as Email;
use classes\gatewayapi as GatewayAPI;
use classes\slack as Slack;
use goals\classes\goals_criteria;
use goals\services\goals_progress_alert_renderer;
use goals\helpers\goals_criteria_progress_alert_destination as Dest;
use objects\departments_o;
use objects\department_goals_o;
use traits\route_t;

class departmentGoalsRoute
{
    use route_t;

    public function run(): void
    {
        // List or get single department goal(s)
        $this->get('/goals/department', function () {
            global /** @var response $response */ $response;
            $this->requirePermission('goals_department_list');

            // Current user and access helpers
            $user = (new authentication())->get_user();
            if (!$user) {
                $response->error('Unauthorized', 401);
            }
            $isSuperuser = method_exists($user, 'hasPermission') && $user->hasPermission('superuser');
            $userDepartments = method_exists($user, 'getGroup') ? (array)$user->getGroup()->getDepartments() : [];

            $hasAllDepartments = function (array $goalDepartments) use ($userDepartments): bool {
                // Ensure ints
                $goalDepartments = array_map('intval', $goalDepartments);
                $userDepartments = array_map('intval', $userDepartments);
                return count(array_diff($goalDepartments, $userDepartments)) === 0;
            };

            $goals = new department_goals_o();

            // Single by id
            if (self::isParametersSet(['id'])) {
                $id = self::getParameter('id');
                if (!is_numeric($id)) {
                    $response->error('id must be numeric', 400);
                }
                $goal = (new department_goals_o())->select((int)$id);
                if (!$goal->exists()) {
                    $response->error('Goal not found', 404);
                }
                // Access control: must have all departments unless superuser
                if (!$isSuperuser && !$hasAllDepartments((array)$goal->departments->value())) {
                    $response->error('You are not allowed to access this goal', 403);
                }
                $response->success($goal->asArray());
            }

            // List with pagination if set (page, limit, search, filters, order supported by db_object_t)
            $list = $goals->listObjectsWithPaginationIfSet(
                function ($row) {
                    return (new department_goals_o())->select((int)$row['id'])->asArray();
                }
            );
            // Filter unauthorized results unless superuser
            if (!$isSuperuser && is_array($list)) {
                $list = array_values(array_filter($list, function ($item) use ($hasAllDepartments) {
                    $deps = isset($item['departments']) && is_array($item['departments']) ? $item['departments'] : [];
                    return $hasAllDepartments($deps);
                }));
            }
            $response->success($list);
        }, [
            'goals_department_list' => 'List department goals or retrieve a single goal when id is provided'
        ]);

        // Create a new department goal
        $this->post('/goals/department', function () {
            global /** @var response $response */ $response;
            $this->requirePermission('goals_department_create');

            // Authenticated user (creator)
            $user = (new authentication())->get_user();
            if (!$user) {
                $response->error('Unauthorized', 401);
            }
            $isSuperuser = method_exists($user, 'hasPermission') && $user->hasPermission('superuser');
            $userDepartments = method_exists($user, 'getGroup') ? (array)$user->getGroup()->getDepartments() : [];

            // Validate input
            $this->requireParameters(['departments', 'criteria']);
            $departmentsInput = self::getParameter('departments');
            $criteriaInput = self::getParameter('criteria');
            $this->requireType($departmentsInput, 'array');
            $this->requireTypeIn($criteriaInput, ['array', 'object']);

            // Build departments objects array
            $departments = [];
            foreach ($departmentsInput as $deptId) {
                if (!is_numeric($deptId)) {
                    $response->error('All department ids must be numeric', 400);
                }
                $dept = (new departments_o())->select((int)$deptId);
                if (!$dept->exists()) {
                    $response->error('Department not found: ' . $deptId, 404);
                }
                $departments[] = $dept;
            }

            // Access control: creator must have all departments unless superuser
            if (!$isSuperuser) {
                $goalDepartments = array_map(fn($d) => (int)$d->id, $departments);
                $missing = array_diff($goalDepartments, array_map('intval', $userDepartments));
                if (count($missing) > 0) {
                    $response->error('You are not allowed to create goals for one or more selected departments', 403);
                }
            }

            // Parse criteria (preserve unicode characters like æ, ø, å)
            $criteria = goals_criteria::fromJson(json_encode($criteriaInput, JSON_UNESCAPED_UNICODE));
            // Create the goal
            $goal = (new department_goals_o())->add($user, $departments, $criteria);
            $response->success($goal->asArray(), 201);
        }, [
            'goals_department_create' => 'Create a new department goal'
        ]);

        // Update an existing department goal
        $this->put('/goals/department', function () {
            global /** @var response $response */ $response;
            $this->requirePermission('goals_department_update');

            $this->requireParameters(['id']);
            $id = self::getParameter('id');
            if (!is_numeric($id)) {
                $response->error('id must be numeric', 400);
            }
            $goal = (new department_goals_o())->select((int)$id);
            if (!$goal->exists()) {
                $response->error('Goal not found', 404);
            }

            // Current user and access helpers
            $user = (new authentication())->get_user();
            if (!$user) {
                $response->error('Unauthorized', 401);
            }
            $isSuperuser = method_exists($user, 'hasPermission') && $user->hasPermission('superuser');
            $isCreator = ((int)$goal->created_by->value()) === (int)$user->id;
            $userDepartments = method_exists($user, 'getGroup') ? (array)$user->getGroup()->getDepartments() : [];
            $hasAllDepartments = function (array $goalDepartments, array $userDepartments) {
                $goalDepartments = array_map('intval', $goalDepartments);
                $userDepartments = array_map('intval', $userDepartments);
                return count(array_diff($goalDepartments, $userDepartments)) === 0;
            };

            // If not superuser or creator, require access to existing goal departments
            if (!$isSuperuser && !$isCreator && !$hasAllDepartments((array)$goal->departments->value(), $userDepartments)) {
                $response->error('You are not allowed to update this goal', 403);
            }

            $dataToUpdate = [];

            // Optional: departments
            if ($response->isRequestParameterSet('departments')) {
                $departmentsInput = self::getParameter('departments');
                $this->requireType($departmentsInput, 'array');
                foreach ($departmentsInput as $deptId) {
                    if (!is_numeric($deptId)) {
                        $response->error('All department ids must be numeric', 400);
                    }
                }
                // If not superuser or creator, ensure new departments are within user departments
                if (!$isSuperuser && !$isCreator) {
                    $missing = array_diff(array_map('intval', $departmentsInput), array_map('intval', $userDepartments));
                    if (count($missing) > 0) {
                        $response->error('You are not allowed to assign one or more selected departments to this goal', 403);
                    }
                }
                // store as ids array in column
                $dataToUpdate['departments'] = array_map('intval', $departmentsInput);
            }

            // Optional: criteria
            if ($response->isRequestParameterSet('criteria')) {
                $criteriaInput = self::getParameter('criteria');
                $this->requireTypeIn($criteriaInput, ['array', 'object']);
                // Preserve unicode characters when (re)encoding criteria
                $criteria = goals_criteria::fromJson(json_encode($criteriaInput, JSON_UNESCAPED_UNICODE));
                $dataToUpdate['criteria'] = $criteria->toArray();
            }

            if (empty($dataToUpdate)) {
                $response->error('No updatable fields provided. Allowed: departments, criteria', 400);
            }

            $goal->update($dataToUpdate);

            $response->success($goal->asArray());
        }, [
            'goals_department_update' => 'Update an existing department goal'
        ]);

        // Delete a department goal (soft delete if supported)
        $this->delete('/goals/department', function () {
            global /** @var response $response */ $response;
            $this->requirePermission('goals_department_delete');

            $this->requireParameters(['id']);
            $id = self::getParameter('id');
            if (!is_numeric($id)) {
                $response->error('id must be numeric', 400);
            }
            $goal = (new department_goals_o())->select((int)$id);
            if (!$goal->exists()) {
                $response->error('Goal not found', 404);
            }

            // Current user and access helpers
            $user = (new authentication())->get_user();
            if (!$user) {
                $response->error('Unauthorized', 401);
            }
            $isSuperuser = method_exists($user, 'hasPermission') && $user->hasPermission('superuser');
            $isCreator = ((int)$goal->created_by->value()) === (int)$user->id;
            $userDepartments = method_exists($user, 'getGroup') ? (array)$user->getGroup()->getDepartments() : [];
            $goalDepartments = (array)$goal->departments->value();
            $hasAll = count(array_diff(array_map('intval', $goalDepartments), array_map('intval', $userDepartments))) === 0;

            if (!$isSuperuser && !$isCreator && !$hasAll) {
                $response->error('You are not allowed to delete this goal', 403);
            }
            $goal->delete();
            $response->success(['message' => 'Deleted']);
        }, [
            'goals_department_delete' => 'Delete a department goal (soft delete when supported)'
        ]);

        // Send a test progress alert for a department goal
        $this->post('/goals/department/progress-alert/test', function () {
            global /** @var response $response */ $response;
            $this->requirePermission('goals_department_progress_alert_test');

            // Required: id of the goal
            $this->requireParameters(['id']);
            $id = self::getParameter('id');
            if (!is_numeric($id)) {
                $response->error('id must be numeric', 400);
            }

            $goal = (new department_goals_o())->select((int)$id);
            if (!$goal->exists()) {
                $response->error('Goal not found', 404);
            }

            // Authorization: reuse list/get policy — must be superuser or have all goal departments
            $user = (new authentication())->get_user();
            if (!$user) {
                $response->error('Unauthorized', 401);
            }
            $isSuperuser = method_exists($user, 'hasPermission') && $user->hasPermission('superuser');
            $userDepartments = method_exists($user, 'getGroup') ? (array)$user->getGroup()->getDepartments() : [];
            $goalDepartments = (array)$goal->departments->value();
            $hasAll = count(array_diff(array_map('intval', $goalDepartments), array_map('intval', $userDepartments))) === 0;
            if (!$isSuperuser && !$hasAll) {
                $response->error('You are not allowed to send progress alerts for this goal', 403);
            }

            // Rebuild criteria from stored array
            $criteriaArray = (array)$goal->criteria->value();
            $criteria = goals_criteria::fromJson(json_encode($criteriaArray, JSON_UNESCAPED_UNICODE));

            // Attach departments to criteria (so renderer can include them)
            $deptObjs = [];
            foreach ($goalDepartments as $deptId) {
                if (is_numeric($deptId)) {
                    $dept = (new departments_o())->select((int)$deptId);
                    if ($dept->exists()) {
                        $deptObjs[] = $dept;
                    }
                }
            }
            if (isset($criteria->departments)) {
                $criteria->departments->set($deptObjs);
            }

            // Allow overriding the destination for testing
            $destination = $criteria->progress_alert_destination ?? Dest::NONE;
            if ($response->isRequestParameterSet('overrideDestination')) {
                $override = (string)self::getParameter('overrideDestination');
                $try = \goals\helpers\goals_criteria_progress_alert_destination::tryFrom($override);
                if ($try !== null) {
                    $destination = $try;
                }
            }

            // Render message
            $message = goals_progress_alert_renderer::render($criteria);

            // Dispatch according to destination
            $providerResponse = null;
            $target = null;
            try {
                switch ($destination) {
                    case Dest::EMAIL:
                        $this->requireParameters(['email_to']);
                        $to = (string)self::getParameter('email_to');
                        $subject = $response->isRequestParameterSet('subject') ? (string)self::getParameter('subject') : 'Goals progress alert';
                        (new Email())->sendEmail($to, 'Goals Tester', $subject, $message, '');
                        $providerResponse = 'Email queued/sent';
                        $target = $to;
                        break;
                    case Dest::SMS:
                        $this->requireParameters(['sms_to']);
                        $smsParam = self::getParameter('sms_to');
                        $numbers = [];
                        if (is_string($smsParam)) {
                            $numbers = array_values(array_filter(array_map('trim', preg_split('/[,;]/', $smsParam))));
                        } elseif (is_array($smsParam)) {
                            $numbers = array_map('strval', $smsParam);
                        } else {
                            $response->error('sms_to must be string or array', 400);
                        }
                        (new GatewayAPI())->send($numbers, $message);
                        $providerResponse = 'SMS sent';
                        $target = $numbers;
                        break;
                    case Dest::SLACK:
                        // Prefer explicit webhook; else allow department_id; else fallback to default webhook
                        if ($response->isRequestParameterSet('slack_webhook')) {
                            $webhook = (string)self::getParameter('slack_webhook');
                            $providerResponse = (new Slack())->send_webhook_message($message, $webhook);
                            $target = $webhook;
                        } elseif ($response->isRequestParameterSet('department_id')) {
                            $deptId = self::getParameter('department_id');
                            if (!is_numeric($deptId)) {
                                $response->error('department_id must be numeric', 400);
                            }
                            (new Slack())->send_department_booking_notification((int)$deptId, $message);
                            $providerResponse = 'Slack message sent to department webhook';
                            $target = (int)$deptId;
                        } elseif (isset($criteria->departments) && is_array($criteria->departments->list()) && count($criteria->departments->list()) > 0) {
                            // Loop through departments and send notifications
                            /** @var departments_o $dept */
                            foreach ( $criteria->departments->list() as $dept) {
                                $slackWebhook = $dept->slack_webhook->value();
                                if (empty($slackWebhook)) {
                                    continue;
                                }
                                //(new Slack())->send_message((string)goals_progress_alert_renderer::render($criteria, $dept), (string)$dept->name->value());
                                (new Slack())->send_webhook_message((string)goals_progress_alert_renderer::render($criteria, $dept), (string)$dept->slack_webhook->value());
                            }
                        } else {
                            (new Slack())->send_message($message);
                            $providerResponse = 'Slack message sent to default webhook';
                        }
                        break;
                    default:
                        $response->error('Unsupported or NONE destination for progress alert', 400);
                }
            } catch (\Exception $e) {
                $response->error('Failed to send progress alert: ' . $e->getMessage(), 500);
            }

            $response->success([
                'id' => (int)$goal->id,
                'destination' => (string)$destination->name,
                'target' => $target,
                'message_preview' => $message,
                'provider_response' => $providerResponse,
            ]);
        }, [
            'goals_department_progress_alert_test' => 'Send a test progress alert for a department goal (actual delivery)'
        ]);
    }
}
