<?php
defined('BASEPATH') OR exit('No direct script access allowed');

require APPPATH . '/libraries/REST_Controller.php';

class BotWebhook extends REST_Controller
{
    private $userId = '';
    private $context = [];

    public function __construct()
    {
        header('Access-Control-Allow-Origin: *');
        header('Access-Control-Allow-Headers: Content-Type, Cache-Control');
        header('Access-Control-Allow-Methods: GET, POST, OPTIONS, PUT');

        if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
            exit;
        }

        parent::__construct();
        $this->load->model('BotRepository_model', 'botRepo');
    }

    public function index_post()
    {
        $payload = $this->getRequestPayload();

        $this->userId = isset($payload['userId']) ? trim((string) $payload['userId']) : '';
        if ($this->userId === '') {
            $this->sendResponse($this->validationResponse('userId es obligatorio.', 0));
            return;
        }

        $this->botRepo->ensureUser($this->userId);

        $state = $this->botRepo->getState($this->userId);
        $this->context = isset($state['context']) && is_array($state['context']) ? $state['context'] : [];

        $hasIncomingCycle = array_key_exists('cycle', $payload) && $payload['cycle'] !== '' && $payload['cycle'] !== null;
        $cycle = $hasIncomingCycle ? (int) $payload['cycle'] : (int) ($state['current_cycle'] ?? 0);
        $text = isset($payload['text']) ? trim((string) $payload['text']) : '';

        $data = $this->handleCycle($cycle, $text);
        $this->sendResponse($data);
    }

    public function index_options()
    {
        $this->set_response(null, REST_Controller::HTTP_NO_CONTENT);
    }

    private function handleCycle($cycle, $text)
    {
        switch ($cycle) {
            case 0:
                return $this->handleInicioSiNo($text);
            case 1:
                return $this->handleNombre($text);
            case 2:
                return $this->handleCedula($text);
            case 3:
                return $this->handleConfirmacionMovilizador($text);
            case 100:
                return $this->handleMenuPrincipal($text);
            case 101:
            case 102:
            case 103:
            case 104:
            case 105:
            case 106:
            case 107:
            case 108:
            case 109:
                return $this->handleRegistroVotantes($cycle, $text);
            case 201:
            case 202:
            case 203:
            case 204:
            case 205:
                return $this->handleRegistroVotos($cycle, $text);
            case 301:
            case 302:
            case 303:
            case 304:
                return $this->handleConsultaEstado($cycle, $text);
            default:
                return $this->validationResponse('Cycle no implementado.', $cycle);
        }
    }

    private function handleInicioSiNo($text)
    {
        $n = $this->normalize($text);

        if ($this->isNegative($n)) {
            return $this->okResponse([
                'next_cycle' => 0,
                'message' => 'Lastima por no ser parte de la familia pero te esperamos en el futuro.',
            ]);
        }

        if ($this->isAffirmative($n)) {
            return $this->okResponse([
                'next_cycle' => 100,
                'message' => 'Somos al sistema oficial de movilizacion para las consultas internas en Colombia. Por favor selecciona una opcion del menu:',
                'menu' => $this->menuOptions(),
            ]);
        }

        return $this->validationResponse('No reconocimos la opcion que ingresaste.', 0);
    }

    private function handleMenuPrincipal($text)
    {
        $n = $this->normalize($text);

        if ($n === '' || $n === 'menu' || $n === 'inicio' || $n === 'hola') {
            return $this->okResponse([
                'next_cycle' => 100,
                'message' => 'Menu principal. Escribe una opcion o su numero.',
                'menu' => $this->menuOptions(),
            ]);
        }

        $option = $this->parseMainMenuOption($text);
        if ($option === 'movilizador') {
            return $this->okResponse(['next_cycle' => 1, 'message' => 'Empecemos con tus datos. Escribe tu nombre completo.']);
        }
        if ($option === 'votantes') {
            return $this->okResponse(['next_cycle' => 101, 'message' => 'Registro de votantes. Responde: Iniciar o Volver al menu.']);
        }
        if ($option === 'voto') {
            return $this->okResponse(['next_cycle' => 201, 'message' => 'Registro de votacion. Responde: Votos o Volver al menu.']);
        }
        if ($option === 'estado') {
            return $this->okResponse(['next_cycle' => 301, 'message' => 'Consulta de estado. Responde: Consultar ahora o Volver al menu.']);
        }

        return $this->validationResponse('No reconocimos la opcion ingresada.', 100, ['menu' => $this->menuOptions()]);
    }

    private function handleNombre($text)
    {
        if ($text === '' || !preg_match('/^[a-zA-Z\s]{5,120}$/', $text)) {
            return $this->validationResponse('Nombre invalido. Usa solo letras y espacios (minimo 5 caracteres).', 1);
        }

        $this->ctxSet('mov_nombre', $text);

        return $this->okResponse([
            'next_cycle' => 2,
            'message' => 'Gracias. Ahora escribe tu numero de cedula (solo numeros, sin puntos ni espacios).',
        ]);
    }

    private function handleCedula($text)
    {
        if (!$this->isCedula($text)) {
            return $this->validationResponse('Cedula invalida. Debe contener solo numeros (5 a 20 digitos).', 2);
        }

        $this->ctxSet('mov_cedula', $text);

        return $this->okResponse([
            'next_cycle' => 3,
            'message' => $this->buildMovilizadorConfirmMessage(),
        ]);
    }

    private function handleConfirmacionMovilizador($text)
    {
        $decision = $this->normalize($text);

        if ($decision === 'confirmar') {
            $nombre = (string) $this->ctxGet('mov_nombre', '');
            $cedula = (string) $this->ctxGet('mov_cedula', '');
            if ($nombre === '' || $cedula === '') {
                return $this->validationResponse('No hay datos suficientes para guardar. Reinicia el registro.', 1);
            }

            $this->botRepo->upsertMovilizadorData($this->userId, $nombre, $cedula);
            $this->ctxUnset(['mov_nombre', 'mov_cedula']);

            return $this->okResponse([
                'next_cycle' => 100,
                'message' => 'Registro exitoso. Volviendo al menu principal.',
                'menu' => $this->menuOptions(),
            ]);
        }

        if ($decision === 'corregir') {
            $this->ctxUnset(['mov_nombre', 'mov_cedula']);
            return $this->okResponse(['next_cycle' => 1, 'message' => 'Entendido. Reiniciemos el registro del movilizador. Escribe tu nombre completo.']);
        }

        return $this->validationResponse('Respuesta no valida. Debes responder Confirmar o Corregir.', 3);
    }

    private function handleRegistroVotantes($cycle, $text)
    {
        switch ($cycle) {
            case 101:
                $decision = $this->normalize($text);
                if ($this->isMenuBack($decision)) {
                    return $this->backToMenu();
                }
                if ($decision === 'iniciar') {
                    return $this->okResponse(['next_cycle' => 102, 'message' => 'Escribe tu numero de cedula para validar que estas registrado como MOVILIZADOR.']);
                }
                return $this->validationResponse('Respuesta no valida. Escribe Iniciar o Volver al menu.', 101);

            case 102:
                if (!$this->isCedula($text)) {
                    return $this->validationResponse('Cedula invalida. Debe tener solo numeros.', 102);
                }
                if (!$this->botRepo->isMovilizadorCedulaForUser($this->userId, $text)) {
                    return $this->validationResponse('Cedula no registrada como movilizador.', 102);
                }
                return $this->okResponse(['next_cycle' => 103, 'message' => 'Escribe el nombre completo del votante.']);

            case 103:
                if (!preg_match('/^[a-zA-Z\s]{5,120}$/', $text)) {
                    return $this->validationResponse('Nombre invalido. Usa solo letras y espacios.', 103);
                }
                $this->ctxSet('vot_nombre', $text);
                return $this->okResponse(['next_cycle' => 104, 'message' => 'Escribe el numero de cedula del votante (solo numeros).']);

            case 104:
                if (!$this->isCedula($text)) {
                    return $this->validationResponse('Cedula invalida. Debe tener solo numeros.', 104);
                }
                if ($this->botRepo->existsVotante($text)) {
                    return $this->validationResponse('Esta cedula ya fue registrada en el sistema. Intenta con otra cedula.', 104);
                }
                $this->ctxSet('vot_cedula', $text);
                return $this->okResponse(['next_cycle' => 105, 'message' => 'Escribe el centro de votacion.']);

            case 105:
                if (strlen($text) < 4 || strlen($text) > 140) {
                    return $this->validationResponse('Centro de votacion invalido.', 105);
                }
                $this->ctxSet('vot_centro', $text);
                return $this->okResponse(['next_cycle' => 106, 'message' => 'Escribe el numero de contacto sin espacios ni signos.']);

            case 106:
                if (!preg_match('/^[0-9]{7,15}$/', $text)) {
                    return $this->validationResponse('Numero de contacto invalido. Solo numeros (7 a 15 digitos).', 106);
                }
                $this->ctxSet('vot_telefono', $text);
                return $this->okResponse(['next_cycle' => 107, 'message' => 'Escribe el usuario de Instagram (sin @).']);

            case 107:
                if (!preg_match('/^[a-zA-Z0-9._]{3,30}$/', $text)) {
                    return $this->validationResponse('Usuario de Instagram invalido.', 107);
                }
                $this->ctxSet('vot_instagram', $text);
                return $this->okResponse(['next_cycle' => 108, 'message' => $this->buildVotanteConfirmMessage()]);

            case 108:
                $decision = $this->normalize($text);
                if ($decision === 'confirmar') {
                    $ok = $this->botRepo->createVotante($this->userId, [
                        'nombre' => (string) $this->ctxGet('vot_nombre', ''),
                        'cedula' => (string) $this->ctxGet('vot_cedula', ''),
                        'centro_votacion' => (string) $this->ctxGet('vot_centro', ''),
                        'telefono' => (string) $this->ctxGet('vot_telefono', ''),
                        'instagram' => (string) $this->ctxGet('vot_instagram', ''),
                    ]);
                    if (!$ok) {
                        return $this->validationResponse('No se pudo guardar el votante. Verifica datos e intenta nuevamente.', 103);
                    }
                    $this->ctxUnset(['vot_nombre', 'vot_cedula', 'vot_centro', 'vot_telefono', 'vot_instagram']);
                    return $this->okResponse(['next_cycle' => 109, 'message' => 'Votante registrado. Deseas registrar otro votante? Responde: Iniciar o Volver al menu.']);
                }
                if ($decision === 'corregir') {
                    $this->ctxUnset(['vot_nombre', 'vot_cedula', 'vot_centro', 'vot_telefono', 'vot_instagram']);
                    return $this->okResponse(['next_cycle' => 103, 'message' => 'De acuerdo. Reiniciamos captura del votante. Escribe nombre completo.']);
                }
                if ($decision === 'cancelar') {
                    $this->ctxUnset(['vot_nombre', 'vot_cedula', 'vot_centro', 'vot_telefono', 'vot_instagram']);
                    return $this->backToMenu();
                }
                return $this->validationResponse('Respuesta no valida. Usa Confirmar, Corregir o Cancelar.', 108);

            case 109:
                $decision = $this->normalize($text);
                if ($decision === 'iniciar') {
                    return $this->okResponse(['next_cycle' => 103, 'message' => 'Escribe el nombre completo del siguiente votante.']);
                }
                if ($this->isMenuBack($decision)) {
                    return $this->backToMenu();
                }
                return $this->validationResponse('Respuesta no valida. Usa Iniciar o Volver al menu.', 109);

            default:
                return $this->validationResponse('Cycle no implementado en registro de votantes.', $cycle);
        }
    }

    private function handleRegistroVotos($cycle, $text)
    {
        switch ($cycle) {
            case 201:
                $decision = $this->normalize($text);
                if ($decision === 'votos' || $decision === 'iniciar') {
                    return $this->okResponse(['next_cycle' => 202, 'message' => 'Escribe tu numero de cedula para validar MOVILIZADOR.']);
                }
                if ($this->isMenuBack($decision)) {
                    return $this->backToMenu();
                }
                return $this->validationResponse('Respuesta no valida. Escribe Votos o Volver al menu.', 201);

            case 202:
                if (!$this->isCedula($text)) {
                    return $this->validationResponse('Cedula invalida. Debe tener solo numeros.', 202);
                }
                if (!$this->botRepo->isMovilizadorCedulaForUser($this->userId, $text)) {
                    return $this->validationResponse('Cedula no registrada como movilizador.', 202);
                }
                if (!$this->botRepo->movilizadorTieneVotantes($this->userId)) {
                    return $this->validationResponse('No tienes votantes registrados. Primero registra votantes.', 202);
                }
                return $this->okResponse(['next_cycle' => 203, 'message' => 'Escribe la cedula del votante que ya voto.']);

            case 203:
                if (!$this->isCedula($text)) {
                    return $this->validationResponse('Cedula invalida. Debe tener solo numeros.', 203);
                }
                if (!$this->botRepo->existsVotante($text)) {
                    return $this->validationResponse('Esta cedula no se encuentra registrada en el sistema.', 203);
                }
                $votante = $this->botRepo->getVotanteByCedulaForUser($this->userId, $text);
                if (!$votante) {
                    return $this->validationResponse('Esta cedula no pertenece a tus votantes registrados.', 203);
                }
                $this->ctxSet('vote_voter_id', (int) $votante['id']);
                return $this->okResponse(['next_cycle' => 204, 'message' => $this->buildVotoConfirmMessage($votante)]);

            case 204:
                $decision = $this->normalize($text);
                if ($decision === 'cancelar') {
                    return $this->okResponse(['next_cycle' => 203, 'message' => 'Operacion cancelada. Escribe otra cedula para registrar voto.']);
                }
                if ($decision !== 'confirmar voto' && $decision !== 'confirmar') {
                    return $this->validationResponse('Respuesta no valida. Usa Confirmar voto o Cancelar.', 204);
                }

                $voterId = (int) $this->ctxGet('vote_voter_id', 0);
                if ($voterId < 1) {
                    return $this->validationResponse('No hay votante seleccionado. Escribe nuevamente la cedula.', 203);
                }
                if ($this->botRepo->votanteYaVoto($voterId)) {
                    return $this->validationResponse('Este votante ya fue registrado como voto.', 203);
                }

                if (!$this->botRepo->createVote($this->userId, $voterId)) {
                    return $this->validationResponse('No se pudo registrar el voto. Intenta nuevamente.', 203);
                }

                $this->ctxUnset(['vote_voter_id']);
                return $this->okResponse(['next_cycle' => 205, 'message' => 'Voto registrado correctamente. Responde: Registrar otro voto o Volver al menu.']);

            case 205:
                $decision = $this->normalize($text);
                if ($decision === 'registrar otro voto' || $decision === 'registrar voto' || $decision === 'otro') {
                    return $this->okResponse(['next_cycle' => 203, 'message' => 'Escribe la cedula del siguiente votante que ya voto.']);
                }
                if ($this->isMenuBack($decision)) {
                    return $this->backToMenu();
                }
                return $this->validationResponse('Respuesta no valida. Usa Registrar otro voto o Volver al menu.', 205);

            default:
                return $this->validationResponse('Cycle no implementado en registro de votacion.', $cycle);
        }
    }

    private function handleConsultaEstado($cycle, $text)
    {
        switch ($cycle) {
            case 301:
                $decision = $this->normalize($text);
                if ($decision === 'consultar ahora' || $decision === 'consultar') {
                    return $this->okResponse(['next_cycle' => 302, 'message' => 'Escribe tu numero de cedula para validar MOVILIZADOR.']);
                }
                if ($this->isMenuBack($decision)) {
                    return $this->backToMenu();
                }
                return $this->validationResponse('Respuesta no valida. Usa Consultar ahora o Volver al menu.', 301);

            case 302:
                if (!$this->isCedula($text)) {
                    return $this->validationResponse('Cedula invalida. Debe tener solo numeros.', 302);
                }
                if (!$this->botRepo->isMovilizadorCedulaForUser($this->userId, $text)) {
                    return $this->validationResponse('Cedula no registrada como movilizador.', 302);
                }
                return $this->okResponse(['next_cycle' => 303, 'message' => 'Escribe la cedula del votante que deseas consultar.']);

            case 303:
                if (!$this->isCedula($text)) {
                    return $this->validationResponse('Cedula invalida. Debe tener solo numeros.', 303);
                }
                if (!$this->botRepo->existsVotante($text)) {
                    return $this->validationResponse('Esta cedula no se encuentra registrada en el sistema.', 303);
                }
                $votante = $this->botRepo->getVotanteByCedulaForUser($this->userId, $text);
                if (!$votante) {
                    return $this->validationResponse('Esta cedula no pertenece a tus votantes registrados.', 303);
                }

                $voterId = (int) $votante['id'];
                if ($this->botRepo->votanteYaVoto($voterId)) {
                    return $this->okResponse(['next_cycle' => 304, 'message' => $this->buildConsultaResultadoMessage($votante, true)]);
                }

                return $this->okResponse(['next_cycle' => 304, 'message' => $this->buildConsultaResultadoMessage($votante, false)]);

            case 304:
                $decision = $this->normalize($text);
                if ($decision === 'consultar otra cedula' || $decision === 'consultar otra' || $decision === 'consultar') {
                    return $this->okResponse(['next_cycle' => 303, 'message' => 'Escribe la siguiente cedula a consultar.']);
                }
                if ($this->isMenuBack($decision)) {
                    return $this->backToMenu();
                }
                return $this->validationResponse('Respuesta no valida. Usa Consultar otra cedula o Volver al menu.', 304);

            default:
                return $this->validationResponse('Cycle no implementado en consulta de estado.', $cycle);
        }
    }

    private function parseMainMenuOption($text)
    {
        $n = $this->normalize($text);

        if (in_array($n, ['1', 'registrar movilizador', 'movilizador'], true)) {
            return 'movilizador';
        }
        if (in_array($n, ['2', 'registrar votantes', 'votantes', 'registrar votante'], true)) {
            return 'votantes';
        }
        if (in_array($n, ['3', 'registrar voto', 'voto', 'votos'], true)) {
            return 'voto';
        }
        if (in_array($n, ['4', 'consultar estado', 'estado', 'consultar estado de votacion'], true)) {
            return 'estado';
        }

        return null;
    }

    private function backToMenu()
    {
        return $this->okResponse([
            'next_cycle' => 100,
            'message' => 'Menu principal. Escribe una opcion o su numero.',
            'menu' => $this->menuOptions(),
        ]);
    }

    private function menuOptions()
    {
        return [
            '1) Registrar movilizador',
            '2) Registrar votantes',
            '3) Registrar voto',
            '4) Consultar estado de votacion',
        ];
    }

    private function isMenuBack($value)
    {
        return in_array($value, ['volver al menu', 'menu', 'volver'], true);
    }

    private function isAffirmative($value)
    {
        return in_array($value, ['si', 's', 'yes'], true);
    }

    private function isNegative($value)
    {
        return in_array($value, ['no', 'n'], true);
    }

    private function getRequestPayload()
    {
        $text = $this->post('text', true);
        $cycle = $this->post('cycle', true);
        $userId = $this->post('userId', true);

        if ($text !== null || $cycle !== null || $userId !== null) {
            return ['text' => $text, 'cycle' => $cycle, 'userId' => $userId];
        }

        $raw = json_decode($this->input->raw_input_stream, true);
        return is_array($raw) ? $raw : [];
    }

    private function isCedula($text)
    {
        return preg_match('/^[0-9]{5,20}$/', (string) $text) === 1;
    }

    private function normalize($text)
    {
        $text = trim(strtolower((string) $text));
        $text = str_replace(['á', 'é', 'í', 'ó', 'ú', 'ñ'], ['a', 'e', 'i', 'o', 'u', 'n'], $text);
        return preg_replace('/\s+/', ' ', $text);
    }

    private function okResponse(array $data)
    {
        return [
            'status' => true,
            'codigo' => 1,
            'message' => 'OK',
            'data' => $data,
            'http_code' => REST_Controller::HTTP_OK,
        ];
    }

    private function validationResponse($message, $cycle, array $extra = [])
    {
        return [
            'status' => false,
            'codigo' => 1,
            'message' => $message,
            'data' => ['cycle' => (int) $cycle] + $extra,
            'http_code' => REST_Controller::HTTP_OK,
        ];
    }

    private function sendResponse(array $payload)
    {
        $httpCode = isset($payload['http_code']) ? (int) $payload['http_code'] : REST_Controller::HTTP_OK;
        unset($payload['http_code']);

        $status = isset($payload['status']) ? (bool) $payload['status'] : false;
        $codigo = isset($payload['codigo']) ? (int) $payload['codigo'] : 1;
        $rawMessage = isset($payload['message']) ? (string) $payload['message'] : '';
        $rawData = isset($payload['data']) ? $payload['data'] : null;

        $message = $this->resolveMessage($rawMessage, $rawData);
        $nextCycle = $this->resolveNextCycle($rawData);

        $this->botRepo->saveState($this->userId, $nextCycle, $this->context);

        $response = [
            'status' => $status,
            'codigo' => $codigo,
            'message' => $message,
            'data' => $message,
            'next_cycle' => $nextCycle,
        ];

        $this->set_response($response, $httpCode);
    }

    private function resolveMessage($rawMessage, $rawData)
    {
        $message = trim((string) $rawMessage);

        if (($message === '' || $message === 'OK') && is_array($rawData) && isset($rawData['message'])) {
            $message = trim((string) $rawData['message']);
        }
        if (($message === '' || $message === 'OK') && is_string($rawData)) {
            $message = trim($rawData);
        }
        if (is_array($rawData) && isset($rawData['menu']) && is_array($rawData['menu']) && count($rawData['menu']) > 0) {
            $message = trim($message . "\n" . implode("\n", $rawData['menu']));
        }

        return $message === '' ? 'Sin mensaje' : $message;
    }

    private function resolveNextCycle($rawData)
    {
        if (is_array($rawData) && isset($rawData['next_cycle'])) {
            return (int) $rawData['next_cycle'];
        }
        if (is_array($rawData) && isset($rawData['cycle'])) {
            return (int) $rawData['cycle'];
        }
        return 0;
    }

    private function buildMovilizadorConfirmMessage()
    {
        $nombre = (string) $this->ctxGet('mov_nombre', '');
        $cedula = (string) $this->ctxGet('mov_cedula', '');

        return "Confirma que tus datos son correctos:\nNombre: {$nombre}\nCedula: {$cedula}\nResponde: Confirmar o Corregir.";
    }

    private function buildVotanteConfirmMessage()
    {
        $nombre = (string) $this->ctxGet('vot_nombre', '');
        $cedula = (string) $this->ctxGet('vot_cedula', '');
        $centro = (string) $this->ctxGet('vot_centro', '');

        return "Confirma la informacion del votante:\nNombre: {$nombre}\nCedula: {$cedula}\nCentro de votacion: {$centro}\nResponde: Confirmar, Corregir o Cancelar.";
    }

    private function buildVotoConfirmMessage(array $votante)
    {
        $nombre = isset($votante['nombre']) ? (string) $votante['nombre'] : '';
        $cedula = isset($votante['cedula']) ? (string) $votante['cedula'] : '';
        $centro = isset($votante['centro_votacion']) ? (string) $votante['centro_votacion'] : '';

        return "Votante encontrado:\nNombre: {$nombre}\nCedula: {$cedula}\nCentro de votacion: {$centro}\nResponde: Confirmar voto o Cancelar.";
    }

    private function buildConsultaResultadoMessage(array $votante, $yaVoto)
    {
        $nombre = isset($votante['nombre']) ? (string) $votante['nombre'] : '';
        $cedula = isset($votante['cedula']) ? (string) $votante['cedula'] : '';
        $centro = isset($votante['centro_votacion']) ? (string) $votante['centro_votacion'] : '';
        $estado = $yaVoto ? 'Ya voto' : 'Aun no registra voto';

        return "Resultado de la consulta:\nNombre: {$nombre}\nCedula: {$cedula}\nCentro de votacion: {$centro}\nEstado: {$estado}\nResponde: Consultar otra cedula o Volver al menu.";
    }

    private function ctxSet($key, $value)
    {
        $this->context[$key] = $value;
    }

    private function ctxGet($key, $default = null)
    {
        return array_key_exists($key, $this->context) ? $this->context[$key] : $default;
    }

    private function ctxUnset(array $keys)
    {
        foreach ($keys as $key) {
            unset($this->context[$key]);
        }
    }
}
