BEGIN;

-- ===== Paciente =====
CREATE TABLE IF NOT EXISTS paciente (
  interno           text PRIMARY KEY NOT NULL,
  tipo_documento    text,
  numero_documento  text,
  primer_apellido   text,
  segundo_apellido  text,
  primer_nombre     text,
  segundo_nombre    text,
  fecha_nacimiento  date,
  edad              numeric,
  sexo              text,
  activo            boolean DEFAULT true
);

CREATE OR REPLACE FUNCTION calcular_edad()
RETURNS trigger AS $$
BEGIN
  NEW.edad := EXTRACT(YEAR FROM age(current_date, NEW.fecha_nacimiento))::int;
  RETURN NEW;
END;
$$ LANGUAGE plpgsql;

DROP TRIGGER IF EXISTS trg_calcular_edad ON paciente;
CREATE TRIGGER trg_calcular_edad
BEFORE INSERT OR UPDATE OF fecha_nacimiento
ON paciente
FOR EACH ROW
EXECUTE FUNCTION calcular_edad();

-- ===== Establecimiento =====
CREATE TABLE IF NOT EXISTS establecimiento (
  codigo_establecimiento text PRIMARY KEY,
  nombre_establecimiento text,
  epc_ciudad             text,
  epc_departamento       text,
  regional               text,
  regional_normalizada   text
);

-- ===== IPS =====
CREATE TABLE IF NOT EXISTS ips (
  id_ips       serial PRIMARY KEY,
  nit          text,
  nombre_ips   text,
  codigo_ips   text,
  direccion    text,
  telefono     text,
  departamento text,
  municipio    text
);

-- ===== Ingreso =====
CREATE TABLE IF NOT EXISTS ingreso (
  interno                text PRIMARY KEY REFERENCES paciente(interno),
  codigo_establecimiento text REFERENCES establecimiento(codigo_establecimiento),
  estado                 text,
  fecha_ingreso          date,
  nacionalidad           text,
  tiempo_reclusion       text
);

-- ===== Autorizante =====
CREATE TABLE IF NOT EXISTS autorizante (
  numero_documento bigint PRIMARY KEY,
  tipo_documento   text NOT NULL,
  nombre           text NOT NULL,
  telefono         text,
  cargo            text,
  activo           boolean DEFAULT true
);

-- ===== Roles y usuarios =====
CREATE TABLE IF NOT EXISTS rol (
  id_rol SERIAL PRIMARY KEY,
  nombre_rol VARCHAR(50) UNIQUE NOT NULL,
  descripcion TEXT,
  fecha_creacion TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE IF NOT EXISTS usuario (
  id_usuario SERIAL PRIMARY KEY,
  username VARCHAR(50) UNIQUE NOT NULL,
  email VARCHAR(100) UNIQUE NOT NULL,
  password_hash VARCHAR(255) NOT NULL,
  nombre_completo VARCHAR(200) NOT NULL,
  id_rol INTEGER NOT NULL REFERENCES rol(id_rol),
  activo BOOLEAN DEFAULT true,
  fecha_creacion TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  ultimo_login TIMESTAMP,
  intentos_fallidos INTEGER DEFAULT 0,
  bloqueado_hasta TIMESTAMP
);

CREATE TABLE IF NOT EXISTS usuario_sede (
  id_usuario_sede SERIAL PRIMARY KEY,
  id_usuario INTEGER NOT NULL REFERENCES usuario(id_usuario),
  codigo_establecimiento VARCHAR(50) NOT NULL REFERENCES establecimiento(codigo_establecimiento),
  fecha_asignacion TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  UNIQUE(id_usuario, codigo_establecimiento)
);

CREATE INDEX IF NOT EXISTS idx_usuario_username ON usuario(username);
CREATE INDEX IF NOT EXISTS idx_usuario_email ON usuario(email);
CREATE INDEX IF NOT EXISTS idx_usuario_rol ON usuario(id_rol);
CREATE INDEX IF NOT EXISTS idx_usuario_sede_usuario ON usuario_sede(id_usuario);
CREATE INDEX IF NOT EXISTS idx_usuario_sede_sede ON usuario_sede(codigo_establecimiento);

-- ===== Consecutivo autorizacion =====
CREATE TABLE IF NOT EXISTS consecutivo_autorizacion (
  id     smallint PRIMARY KEY,
  codigo text NOT NULL
);

CREATE OR REPLACE FUNCTION next_numero_autorizacion()
RETURNS text AS $$
DECLARE
  actual  text;
  prefijo constant text := 'UTUSCPG';
  letra   text;
  num     int;
  nuevo   text;
BEGIN
  SELECT codigo INTO actual
  FROM consecutivo_autorizacion
  WHERE id = 1
  FOR UPDATE;

  IF actual IS NULL THEN
    actual := prefijo || 'B00';
  END IF;

  letra := substring(actual from 8 for 1);
  num   := substring(actual from 9 for 2)::int;

  IF num < 99 THEN
    num := num + 1;
  ELSE
    letra := chr(ascii(letra) + 1);
    num := 1;
  END IF;

  nuevo := prefijo || letra || LPAD(num::text, 2, '0');

  UPDATE consecutivo_autorizacion
  SET codigo = nuevo
  WHERE id = 1;

  RETURN nuevo;
END;
$$ LANGUAGE plpgsql;

-- ===== Autorizacion =====
CREATE TABLE IF NOT EXISTS autorizacion (
  numero_autorizacion text PRIMARY KEY DEFAULT next_numero_autorizacion(),
  interno             text NOT NULL REFERENCES paciente(interno),
  id_ips              integer NOT NULL REFERENCES ips(id_ips),
  numero_documento_autorizante bigint NOT NULL REFERENCES autorizante(numero_documento),
  fecha_autorizacion  date NOT NULL DEFAULT current_date,
  observacion         text,
  cup_codigo          varchar(20),
  tipo_autorizacion   varchar(50) NOT NULL DEFAULT 'consultas_externas',
  tipo_servicio       varchar(50),
  version             integer NOT NULL DEFAULT 1
);

INSERT INTO consecutivo_autorizacion (id, codigo)
VALUES (1, COALESCE((SELECT MAX(numero_autorizacion) FROM autorizacion), 'UTUSCPGB00'))
ON CONFLICT (id) DO NOTHING;

CREATE OR REPLACE FUNCTION asegurar_numero_unico()
RETURNS TRIGGER AS $$
BEGIN
  IF NEW.numero_autorizacion IS NULL OR NEW.numero_autorizacion = '' THEN
    NEW.numero_autorizacion := next_numero_autorizacion();
  END IF;
  RETURN NEW;
END;
$$ LANGUAGE plpgsql;

DROP TRIGGER IF EXISTS trg_asegurar_numero_unico ON autorizacion;
CREATE TRIGGER trg_asegurar_numero_unico
BEFORE INSERT ON autorizacion
FOR EACH ROW
EXECUTE FUNCTION asegurar_numero_unico();

-- ===== Versiones de autorizacion =====
CREATE TABLE IF NOT EXISTS autorizacion_version (
  id SERIAL PRIMARY KEY,
  numero_autorizacion VARCHAR(50) NOT NULL,
  version INTEGER NOT NULL,
  id_ips INTEGER NOT NULL,
  numero_documento_autorizante BIGINT NOT NULL,
  fecha_autorizacion DATE,
  observacion TEXT,
  cup_codigo VARCHAR(20),
  tipo_autorizacion VARCHAR(50),
  tipo_servicio VARCHAR(50),
  fecha_version TIMESTAMP NOT NULL DEFAULT NOW()
);

CREATE UNIQUE INDEX IF NOT EXISTS autorizacion_version_unique
ON autorizacion_version (numero_autorizacion, version);

COMMIT;
