<?php
require_once "vendor/autoload.php";

use \SeinopSys\PostgresDb;
# env
$dotenv = Dotenv\Dotenv::createImmutable(__DIR__);
$dotenv->load();

# Connect to the database
try {
    // Postgres
    $pdo = new PDO("pgsql:host={$_ENV['DB_HOST']};dbname={$_ENV['DB_NAME']}", $_ENV['DB_USER'], $_ENV['DB_PASS']);
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

    $db = new PostgresDb();
    $db->setConnection($pdo);
} catch (PDOException $e) {
    echo "Error: " . $e->getMessage();
}


trait DatabaseModel
{

    public function __construct(protected string $tableName, protected array $columns = [])
    {
    }

    public function get(array $params = [], string $what = '*')
    {
        global $db;

        $conditions = [];
        foreach ($params as $key => $value) {
            $conditions[] = "$key = :$key";
        }

        $sql = "SELECT $what FROM $this->tableName";
        if ($conditions) {
            $sql .= " WHERE " . implode(" AND ", $conditions);
        }

        $result = $db->query($sql, $params);
        return $result;
    }

    protected function insert__(array $params = [], ?string $where = null)
    {
        global $db;

        if ($where === null) {
            $where = $this->tableName;
        }

        $columns = [];
        foreach ($params as $key => $value) {
            $columns[] = "$key = :$key";
        }

        $sql = "INSERT INTO $where SET " . implode(", ", $columns);
        $result = $db->query($sql, $params);
        return $result;

    }
}

abstract class WebServiceSGU
{
    const BASE_URL = "https://portal.ulsa.edu.mx/servicios/AuditoriaAsistencialRest/AuditoriaAsistencialService.svc/auditoriaAsistencial";

    private static array $keys = [
        'username' => 'SGU_APSA_AUD_ASIST',
        'password' => 'B4qa594JFPr2ufHrZdHS8A==',
    ];

    private static ?JsonSchema\Validator $validator = null;
    private string $baseUrl;

    public function __construct(protected string $endpoint, protected ?string $schema = null)
    {
        $this->baseUrl = self::BASE_URL . $endpoint;
    }

    private static function initCurl(array $options = [])
    {
        $ch = curl_init();
        curl_setopt_array($ch, $options);
        return $ch;
    }

    private static function get_token(): string
    {
        $curl = self::initCurl([
            CURLOPT_URL => self::BASE_URL . "/inicioSesion/seleccionar",
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_CUSTOMREQUEST => "POST",
            CURLOPT_HTTPHEADER => ["Content-Type: application/json"],
            CURLOPT_POSTFIELDS => json_encode(self::$keys),
        ]);

        $response = curl_exec($curl);
        $err = curl_error($curl);
        curl_close($curl);

        if ($err)
            throw new Exception("cURL Error: $err");

        return trim($response, '"'); // remove quotes
    }

    protected function validate_schema($data): bool
    {
        if ($this->schema === null)
            return true;

        self::getValidator()->validate($data, (object) json_decode($this->schema));
        return self::getValidator()->isValid();
    }

    public static function getValidator(): JsonSchema\Validator
    {
        return self::$validator ??= new JsonSchema\Validator();
    }

    public function get_errors(): array
    {
        return self::getValidator()->getErrors();
    }

    public function get(array $data = []): array
    {
        if (!$this->validate_schema($data)) {
            throw new Exception('Invalid schema');
        }

        $ch = self::initCurl([
            CURLOPT_POST => 1,
            CURLOPT_POSTFIELDS => json_encode($data),
            CURLOPT_URL => $this->baseUrl,
            CURLOPT_HTTPHEADER => [
                'Content-Type: application/json',
                'Accept: application/json',
                'username: ' . self::$keys['username'],
                'token: ' . self::get_token(),
            ],
            CURLOPT_RETURNTRANSFER => 1,
        ]);

        $response = curl_exec($ch);
        if (curl_errno($ch)) {
            throw new Exception('cURL Error: ' . curl_error($ch));
        }

        curl_close($ch);

        $response = json_decode($response, true);

        if ($response === null) {
            throw new Exception('Invalid response');
        }

        return $response;
    }
}

final class Horarios extends WebServiceSGU
{
    public function __construct()
    {
        parent::__construct(
            "/seleccionar",
            <<<JSON
                {
                    "\$schema": "http://json-schema.org/draft-07/schema#",
                    "type": "object",
                    "required": ["idPeriodo"],
                    "properties": {
                        "idPeriodo": {
                            "type": "integer",
                            "description": "Identificador del periodo a consultar."
                        },
                        "claveFacultad": {
                            "type": "string",
                            "description": "Clave de la facultad a consultar.",
                            "pattern": "^[a-zA-Z0-9]*$"
                        },
                        "claveCarrera": {
                            "type": "string",
                            "description": "Clave de la carrera a consultar.",
                            "pattern": "^[a-zA-Z0-9]*$"
                        },
                        "claveProfesor": {
                            "type": "string",
                            "description": "Clave del empleado a consultar.",
                            "pattern": "^[a-zA-Z0-9]*$"
                        },
                        "fecha": {
                            "type": "string",
                            "description": "Fecha de la clase.",
                            "pattern": "^\\d{4}-\\d{2}-\\d{2}$"
                        }
                    }
                }
            JSON
        );
    }
}