-- ============================================================================
-- Architektur-Planungs-App Datenbank Schema
-- PostgreSQL 15+ mit PostGIS 3.4+
-- Schweizer Koordinatensystem: LV95 (EPSG:2056)
-- ============================================================================

-- PostGIS Extension aktivieren
CREATE EXTENSION IF NOT EXISTS postgis;

-- UUID Extension für uuid_generate_v4()
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";

-- ============================================================================
-- ENUMS
-- ============================================================================

CREATE TYPE status_prozess AS ENUM (
    'Eingang',
    'Analyse',
    'Studie',
    'Planung',
    'Baurechtsverfahren',
    'Umsetzung',
    'Archiv'
);

CREATE TYPE dokument_typ AS ENUM (
    'Datei',
    'Url'
);

CREATE TYPE tag_typ AS ENUM (
    'Kataster Objekte',
    'Kataster Werkeleitungen',
    'Kataster Belastete Standorte',
    'Kataster Bäume',
    'Zonenplan',
    'Planungs- und Baugesetz (PGB)',
    'Bau- und Zonenordnung (BZO)',
    'Parkplatzverordnung',
    'Eigentümerauskunft',
    'Grundbuchauszug'
);

CREATE TYPE geo_tag_typ AS ENUM (
    'Referenzpunkt Kat. 1',
    'Referenzpunkt Kat. 2',
    'Referenzpunkt Kat. 3',
    'Geometeraufnahme'
);

CREATE TYPE ja_nein AS ENUM (
    '',
    'Ja',
    'Nein'
);

-- ============================================================================
-- HAUPTTABELLEN
-- ============================================================================

-- Land
CREATE TABLE land (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    label VARCHAR(255) NOT NULL,
    created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);

-- Kanton
CREATE TABLE kanton (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    label VARCHAR(255) NOT NULL,
    baureglement_aktuell_id UUID,
    baureglement_revision_id UUID,
    bauverordnung_aktuell_id UUID,
    bauverordnung_revision_id UUID,
    created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);

-- Gemeinde
CREATE TABLE gemeinde (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    label VARCHAR(255) NOT NULL,
    plz VARCHAR(10),
    bzo_aktuell_id UUID,
    bzo_revision_id UUID,
    created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);

-- Dokument
CREATE TABLE dokument (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    label VARCHAR(255) NOT NULL,
    versionsbezeichnung VARCHAR(100),
    typ dokument_typ NOT NULL,
    format VARCHAR(50),
    dokument_referenz TEXT NOT NULL,
    tags tag_typ[],
    created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);

-- Projekt
CREATE TABLE projekt (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    label VARCHAR(255) NOT NULL,
    status_prozess status_prozess[],
    created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);

-- Parzelle
CREATE TABLE parzelle (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    label VARCHAR(255) NOT NULL,
    parzellen_nummern VARCHAR(50)[],
    eigentuemerschaaft VARCHAR(255),
    strasse_nr VARCHAR(255),
    
    -- Geografischer Kontext
    land_id UUID REFERENCES land(id),
    kanton_id UUID REFERENCES kanton(id),
    gemeinde_id UUID REFERENCES gemeinde(id),
    
    -- Geometrie (PostGIS)
    geo_umfang GEOMETRY(POLYGON, 2056),
    
    -- Bauliche Parameter
    bauzone VARCHAR(50),
    az DECIMAL(5,2),
    bz DECIMAL(5,2),
    vollgeschoss_zahl INTEGER,
    anrechenbar_dachgeschoss DECIMAL(3,2),
    anrechenbar_untergeschoss DECIMAL(3,2),
    gebaeudehoehe_max DECIMAL(6,2),
    
    -- Regelungen
    regeln_grenzabstand TEXT,
    regeln_mehrlaengenzuschlag TEXT,
    regeln_mehrhoehenzuschlag TEXT,
    
    -- Schutzzonen
    hochwasserschutzzone VARCHAR(100),
    laermschutzzone VARCHAR(100),
    grundwasserschutzzone VARCHAR(100),
    
    -- Eigenschaften
    parzelle_bebaut ja_nein,
    parzelle_erschlossen ja_nein,
    hanglage ja_nein,
    
    created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);

-- GeoPunkt
CREATE TABLE geo_punkt (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    x DECIMAL(12,3) NOT NULL,  -- LV95 Ostwert
    y DECIMAL(12,3) NOT NULL,  -- LV95 Nordwert
    z DECIMAL(8,3),            -- Höhe über Meer
    referenzen geo_tag_typ[],
    projekt_id UUID REFERENCES projekt(id) ON DELETE CASCADE,
    created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);

-- Kontext (Polymorphe Beziehung)
CREATE TABLE kontext (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    thema VARCHAR(255) NOT NULL,
    inhalt TEXT NOT NULL,
    
    -- Polymorphe Foreign Keys
    projekt_id UUID REFERENCES projekt(id) ON DELETE CASCADE,
    parzelle_id UUID REFERENCES parzelle(id) ON DELETE CASCADE,
    land_id UUID REFERENCES land(id) ON DELETE CASCADE,
    kanton_id UUID REFERENCES kanton(id) ON DELETE CASCADE,
    gemeinde_id UUID REFERENCES gemeinde(id) ON DELETE CASCADE,
    
    created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
    
    -- Constraint: Kontext muss zu genau einer Entität gehören
    CONSTRAINT kontext_single_parent CHECK (
        (projekt_id IS NOT NULL)::INTEGER +
        (parzelle_id IS NOT NULL)::INTEGER +
        (land_id IS NOT NULL)::INTEGER +
        (kanton_id IS NOT NULL)::INTEGER +
        (gemeinde_id IS NOT NULL)::INTEGER = 1
    )
);

-- ============================================================================
-- JUNCTION TABLES (Many-to-Many Beziehungen)
-- ============================================================================

-- Projekt <-> Parzelle
CREATE TABLE projekt_parzelle (
    projekt_id UUID NOT NULL REFERENCES projekt(id) ON DELETE CASCADE,
    parzelle_id UUID NOT NULL REFERENCES parzelle(id) ON DELETE CASCADE,
    PRIMARY KEY (projekt_id, parzelle_id)
);

-- Projekt <-> Dokument (Bauherrschaft)
CREATE TABLE projekt_dokument_bauherrschaft (
    projekt_id UUID NOT NULL REFERENCES projekt(id) ON DELETE CASCADE,
    dokument_id UUID NOT NULL REFERENCES dokument(id) ON DELETE CASCADE,
    PRIMARY KEY (projekt_id, dokument_id)
);

-- Projekt <-> Dokument (Planung)
CREATE TABLE projekt_dokument_planung (
    projekt_id UUID NOT NULL REFERENCES projekt(id) ON DELETE CASCADE,
    dokument_id UUID NOT NULL REFERENCES dokument(id) ON DELETE CASCADE,
    PRIMARY KEY (projekt_id, dokument_id)
);

-- Parzelle <-> Parzelle (Nachbarn)
CREATE TABLE parzelle_nachbar (
    parzelle_id UUID NOT NULL REFERENCES parzelle(id) ON DELETE CASCADE,
    nachbar_id UUID NOT NULL REFERENCES parzelle(id) ON DELETE CASCADE,
    PRIMARY KEY (parzelle_id, nachbar_id),
    CHECK (parzelle_id != nachbar_id)  -- Parzelle kann nicht ihr eigener Nachbar sein
);

-- Parzelle <-> Dokument
CREATE TABLE parzelle_dokument (
    parzelle_id UUID NOT NULL REFERENCES parzelle(id) ON DELETE CASCADE,
    dokument_id UUID NOT NULL REFERENCES dokument(id) ON DELETE CASCADE,
    PRIMARY KEY (parzelle_id, dokument_id)
);

-- ============================================================================
-- FOREIGN KEY CONSTRAINTS (nachträglich für Kanton/Gemeinde Dokumente)
-- ============================================================================

ALTER TABLE kanton
    ADD CONSTRAINT fk_kanton_baureglement_aktuell
        FOREIGN KEY (baureglement_aktuell_id) REFERENCES dokument(id),
    ADD CONSTRAINT fk_kanton_baureglement_revision
        FOREIGN KEY (baureglement_revision_id) REFERENCES dokument(id),
    ADD CONSTRAINT fk_kanton_bauverordnung_aktuell
        FOREIGN KEY (bauverordnung_aktuell_id) REFERENCES dokument(id),
    ADD CONSTRAINT fk_kanton_bauverordnung_revision
        FOREIGN KEY (bauverordnung_revision_id) REFERENCES dokument(id);

ALTER TABLE gemeinde
    ADD CONSTRAINT fk_gemeinde_bzo_aktuell
        FOREIGN KEY (bzo_aktuell_id) REFERENCES dokument(id),
    ADD CONSTRAINT fk_gemeinde_bzo_revision
        FOREIGN KEY (bzo_revision_id) REFERENCES dokument(id);

-- ============================================================================
-- INDICES für Performance
-- ============================================================================

-- Projekt Indices
CREATE INDEX idx_projekt_status ON projekt USING GIN (status_prozess);

-- Parzelle Indices
CREATE INDEX idx_parzelle_land ON parzelle(land_id);
CREATE INDEX idx_parzelle_kanton ON parzelle(kanton_id);
CREATE INDEX idx_parzelle_gemeinde ON parzelle(gemeinde_id);
CREATE INDEX idx_parzelle_bauzone ON parzelle(bauzone);
CREATE INDEX idx_parzelle_geo_umfang ON parzelle USING GIST(geo_umfang);

-- Dokument Indices
CREATE INDEX idx_dokument_typ ON dokument(typ);
CREATE INDEX idx_dokument_tags ON dokument USING GIN (tags);

-- GeoPunkt Indices
CREATE INDEX idx_geopunkt_projekt ON geo_punkt(projekt_id);
CREATE INDEX idx_geopunkt_referenzen ON geo_punkt USING GIN (referenzen);

-- Kontext Indices
CREATE INDEX idx_kontext_projekt ON kontext(projekt_id);
CREATE INDEX idx_kontext_parzelle ON kontext(parzelle_id);
CREATE INDEX idx_kontext_land ON kontext(land_id);
CREATE INDEX idx_kontext_kanton ON kontext(kanton_id);
CREATE INDEX idx_kontext_gemeinde ON kontext(gemeinde_id);
CREATE INDEX idx_kontext_thema ON kontext(thema);

-- ============================================================================
-- TRIGGER für updated_at
-- ============================================================================

CREATE OR REPLACE FUNCTION update_updated_at_column()
RETURNS TRIGGER AS $$
BEGIN
    NEW.updated_at = CURRENT_TIMESTAMP;
    RETURN NEW;
END;
$$ language 'plpgsql';

CREATE TRIGGER update_land_updated_at BEFORE UPDATE ON land
    FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();

CREATE TRIGGER update_kanton_updated_at BEFORE UPDATE ON kanton
    FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();

CREATE TRIGGER update_gemeinde_updated_at BEFORE UPDATE ON gemeinde
    FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();

CREATE TRIGGER update_dokument_updated_at BEFORE UPDATE ON dokument
    FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();

CREATE TRIGGER update_projekt_updated_at BEFORE UPDATE ON projekt
    FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();

CREATE TRIGGER update_parzelle_updated_at BEFORE UPDATE ON parzelle
    FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();

CREATE TRIGGER update_geo_punkt_updated_at BEFORE UPDATE ON geo_punkt
    FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();

CREATE TRIGGER update_kontext_updated_at BEFORE UPDATE ON kontext
    FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();

-- ============================================================================
-- VIEWS für häufige Abfragen
-- ============================================================================

-- View: Parzellen mit vollständigem geografischem Kontext
CREATE VIEW v_parzelle_vollstaendig AS
SELECT 
    p.*,
    l.label as land_name,
    k.label as kanton_name,
    g.label as gemeinde_name,
    g.plz as gemeinde_plz,
    ST_AsGeoJSON(p.geo_umfang) as geo_umfang_geojson,
    ST_Area(p.geo_umfang) as flaeche_m2
FROM parzelle p
LEFT JOIN land l ON p.land_id = l.id
LEFT JOIN kanton k ON p.kanton_id = k.id
LEFT JOIN gemeinde g ON p.gemeinde_id = g.id;

-- View: Projekte mit Perimeter-Information
CREATE VIEW v_projekt_mit_perimeter AS
SELECT 
    pr.id,
    pr.label,
    pr.status_prozess,
    COUNT(DISTINCT pp.parzelle_id) as anzahl_parzellen,
    STRING_AGG(DISTINCT pa.label, ', ') as parzellen_labels
FROM projekt pr
LEFT JOIN projekt_parzelle pp ON pr.id = pp.projekt_id
LEFT JOIN parzelle pa ON pp.parzelle_id = pa.id
GROUP BY pr.id, pr.label, pr.status_prozess;

-- ============================================================================
-- BEISPIELDATEN
-- ============================================================================

-- Land Schweiz
INSERT INTO land (label) VALUES ('Schweiz');

-- Kantone (Beispiele)
INSERT INTO kanton (label) VALUES 
    ('Zürich'),
    ('Bern'),
    ('Luzern');

-- Gemeinden (Beispiele für Zürich)
INSERT INTO gemeinde (label, plz) VALUES
    ('Zürich', '8000'),
    ('Winterthur', '8400'),
    ('Uster', '8610');

-- ============================================================================
-- KOMMENTARE
-- ============================================================================

COMMENT ON TABLE projekt IS 'Bauprojekte mit Status und Perimeter';
COMMENT ON TABLE parzelle IS 'Grundstücke mit baulichen und rechtlichen Eigenschaften';
COMMENT ON TABLE dokument IS 'Dokumente und URLs mit Versionierung';
COMMENT ON TABLE geo_punkt IS '3D-Punkte im LV95-Koordinatensystem';
COMMENT ON TABLE kontext IS 'Flexible Kontextinformationen für verschiedene Entitäten';

COMMENT ON COLUMN parzelle.geo_umfang IS 'Parzellengrenze als PostGIS Polygon im LV95 (EPSG:2056)';
COMMENT ON COLUMN parzelle.az IS 'Ausnützungsziffer';
COMMENT ON COLUMN parzelle.bz IS 'Bebauungsziffer';
COMMENT ON COLUMN geo_punkt.x IS 'LV95 Ostwert (E), typisch 2480000-2840000';
COMMENT ON COLUMN geo_punkt.y IS 'LV95 Nordwert (N), typisch 1070000-1300000';
COMMENT ON COLUMN geo_punkt.z IS 'Höhe über Meer in Metern';

-- ============================================================================
-- Ende der Migration
-- ============================================================================
