-- BikepackPilot database draft
-- Requires PostgreSQL + PostGIS.

CREATE EXTENSION IF NOT EXISTS postgis;
CREATE EXTENSION IF NOT EXISTS pgcrypto;

CREATE TYPE confidence_level AS ENUM (
  'known_good', 'high', 'medium', 'low', 'unknown', 'restricted', 'conflicting'
);

CREATE TYPE warning_severity AS ENUM ('info', 'notice', 'warning', 'critical');

CREATE TYPE poi_category AS ENUM (
  'water', 'food', 'sleep', 'repair', 'bailout', 'charging', 'emergency'
);

CREATE TABLE users (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  email TEXT UNIQUE,
  display_name TEXT,
  default_profile TEXT NOT NULL DEFAULT 'loaded_gravel',
  privacy_mode TEXT NOT NULL DEFAULT 'private',
  created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
  updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
);

CREATE TABLE trips (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  owner_id UUID REFERENCES users(id) ON DELETE SET NULL,
  title TEXT NOT NULL,
  start_point GEOGRAPHY(Point, 4326) NOT NULL,
  end_point GEOGRAPHY(Point, 4326) NOT NULL,
  start_date DATE,
  end_date DATE,
  profile TEXT NOT NULL,
  constraints JSONB NOT NULL DEFAULT '{}'::jsonb,
  status TEXT NOT NULL DEFAULT 'draft',
  created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
  updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
);

CREATE TABLE routes (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  trip_id UUID NOT NULL REFERENCES trips(id) ON DELETE CASCADE,
  provider TEXT NOT NULL,
  geometry GEOGRAPHY(LineString, 4326) NOT NULL,
  distance_m INTEGER NOT NULL,
  ascent_m INTEGER DEFAULT 0,
  descent_m INTEGER DEFAULT 0,
  surface_breakdown JSONB NOT NULL DEFAULT '{}'::jsonb,
  suitability_score NUMERIC(5,2),
  warnings JSONB NOT NULL DEFAULT '[]'::jsonb,
  attribution TEXT,
  created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);

CREATE TABLE route_segments (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  route_id UUID NOT NULL REFERENCES routes(id) ON DELETE CASCADE,
  seq INTEGER NOT NULL,
  start_m INTEGER NOT NULL,
  end_m INTEGER NOT NULL,
  geometry GEOGRAPHY(LineString, 4326) NOT NULL,
  distance_m INTEGER NOT NULL,
  ascent_m INTEGER DEFAULT 0,
  descent_m INTEGER DEFAULT 0,
  avg_grade_percent NUMERIC(5,2),
  max_grade_percent NUMERIC(5,2),
  surface TEXT,
  smoothness TEXT,
  highway TEXT,
  bicycle_access TEXT,
  legal_access_confidence confidence_level NOT NULL DEFAULT 'unknown',
  source_confidence confidence_level NOT NULL DEFAULT 'unknown',
  traffic_stress NUMERIC(4,2),
  official_cycle_route BOOLEAN DEFAULT false,
  protected_area_overlap BOOLEAN DEFAULT false,
  scores JSONB NOT NULL DEFAULT '{}'::jsonb
);

CREATE INDEX route_segments_route_seq_idx ON route_segments(route_id, seq);
CREATE INDEX route_segments_geom_idx ON route_segments USING GIST(geometry);

CREATE TABLE pois (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  category poi_category NOT NULL,
  name TEXT,
  location GEOGRAPHY(Point, 4326) NOT NULL,
  source TEXT NOT NULL,
  source_id TEXT,
  confidence confidence_level NOT NULL DEFAULT 'unknown',
  opening_hours TEXT,
  last_seen_at TIMESTAMPTZ,
  last_confirmed_by_user_at TIMESTAMPTZ,
  tags JSONB NOT NULL DEFAULT '{}'::jsonb,
  created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
  updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
);

CREATE INDEX pois_category_idx ON pois(category);
CREATE INDEX pois_location_idx ON pois USING GIST(location);
CREATE UNIQUE INDEX pois_source_source_id_idx ON pois(source, source_id) WHERE source_id IS NOT NULL;

CREATE TABLE route_pois (
  route_id UUID NOT NULL REFERENCES routes(id) ON DELETE CASCADE,
  poi_id UUID NOT NULL REFERENCES pois(id) ON DELETE CASCADE,
  meters_from_start INTEGER,
  distance_from_route_m INTEGER,
  detour_distance_m INTEGER,
  relevance_score NUMERIC(5,2),
  PRIMARY KEY (route_id, poi_id)
);

CREATE TABLE stages (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  route_id UUID NOT NULL REFERENCES routes(id) ON DELETE CASCADE,
  day_index INTEGER NOT NULL,
  start_m INTEGER NOT NULL,
  end_m INTEGER NOT NULL,
  distance_m INTEGER NOT NULL,
  ascent_m INTEGER DEFAULT 0,
  descent_m INTEGER DEFAULT 0,
  endpoint GEOGRAPHY(Point, 4326) NOT NULL,
  sleep_candidates JSONB NOT NULL DEFAULT '[]'::jsonb,
  water_pois JSONB NOT NULL DEFAULT '[]'::jsonb,
  food_pois JSONB NOT NULL DEFAULT '[]'::jsonb,
  repair_pois JSONB NOT NULL DEFAULT '[]'::jsonb,
  bailout_pois JSONB NOT NULL DEFAULT '[]'::jsonb,
  warnings JSONB NOT NULL DEFAULT '[]'::jsonb,
  plan_b JSONB,
  created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);

CREATE TABLE service_gaps (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  route_id UUID NOT NULL REFERENCES routes(id) ON DELETE CASCADE,
  category poi_category NOT NULL,
  start_m INTEGER NOT NULL,
  end_m INTEGER NOT NULL,
  distance_m INTEGER NOT NULL,
  severity warning_severity NOT NULL,
  explanation TEXT NOT NULL
);

CREATE TABLE rule_cards (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  rule_type TEXT NOT NULL,
  jurisdiction TEXT NOT NULL,
  status TEXT NOT NULL,
  confidence confidence_level NOT NULL DEFAULT 'unknown',
  summary TEXT NOT NULL,
  source_url TEXT,
  source_name TEXT,
  last_reviewed_at DATE,
  valid_from DATE,
  valid_to DATE,
  geometry GEOGRAPHY(Geometry, 4326),
  created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);

CREATE INDEX rule_cards_geom_idx ON rule_cards USING GIST(geometry);

CREATE TABLE local_reports (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  user_id UUID REFERENCES users(id) ON DELETE SET NULL,
  route_id UUID REFERENCES routes(id) ON DELETE SET NULL,
  route_segment_id UUID REFERENCES route_segments(id) ON DELETE SET NULL,
  report_type TEXT NOT NULL,
  location GEOGRAPHY(Point, 4326) NOT NULL,
  meters_from_start INTEGER,
  observed_at TIMESTAMPTZ NOT NULL,
  details TEXT,
  payload JSONB NOT NULL DEFAULT '{}'::jsonb,
  trust_score NUMERIC(5,2) DEFAULT 0,
  expires_at TIMESTAMPTZ,
  moderation_status TEXT NOT NULL DEFAULT 'pending',
  created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);

CREATE INDEX local_reports_location_idx ON local_reports USING GIST(location);
CREATE INDEX local_reports_type_idx ON local_reports(report_type);

CREATE TABLE offline_packs (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  trip_id UUID NOT NULL REFERENCES trips(id) ON DELETE CASCADE,
  route_id UUID NOT NULL REFERENCES routes(id) ON DELETE CASCADE,
  bbox GEOGRAPHY(Polygon, 4326),
  route_buffer_km NUMERIC(6,2) NOT NULL DEFAULT 5,
  included_layers JSONB NOT NULL DEFAULT '[]'::jsonb,
  data_versions JSONB NOT NULL DEFAULT '{}'::jsonb,
  files JSONB NOT NULL DEFAULT '[]'::jsonb,
  warnings JSONB NOT NULL DEFAULT '[]'::jsonb,
  status TEXT NOT NULL DEFAULT 'queued',
  created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
  expires_at TIMESTAMPTZ
);
