from contextlib import contextmanager
import psycopg2
import psycopg2.extras
from config import Config


def get_connection():
    return psycopg2.connect(
        host=Config.DB_HOST,
        dbname=Config.DB_NAME,
        user=Config.DB_USER,
        password=Config.DB_PASSWORD,
        port=Config.DB_PORT,
        cursor_factory=psycopg2.extras.RealDictCursor,
    )


@contextmanager
def get_cursor(commit=False):
    conn = get_connection()
    cur = conn.cursor()
    try:
        yield cur
        if commit:
            conn.commit()
    except Exception:
        conn.rollback()
        raise
    finally:
        cur.close()
        conn.close()


def fetch_one(query, params=None):
    with get_cursor() as cur:
        cur.execute(query, params or ())
        return cur.fetchone()


def fetch_all(query, params=None):
    with get_cursor() as cur:
        cur.execute(query, params or ())
        return cur.fetchall()


def execute(query, params=None):
    with get_cursor(commit=True) as cur:
        cur.execute(query, params or ())


def execute_returning(query, params=None):
    with get_cursor(commit=True) as cur:
        cur.execute(query, params or ())
        return cur.fetchone()


###############
# Benutzer
###############

def get_user_by_id(user_id):
    return fetch_one(
        """
        SELECT id, name, email, passwort_hash, is_active, last_login
        FROM benutzer
        WHERE id = %s
        """,
        (user_id,),
    )


def get_user_by_email(email):
    return fetch_one(
        """
        SELECT id, name, email, passwort_hash, is_active, last_login
        FROM benutzer
        WHERE email = %s
        """,
        (email,),
    )


def get_all_users():
    return fetch_all(
        """
        SELECT id, name, email, last_login, is_active
        FROM benutzer
        ORDER BY name, email
        """
    )


def create_user(name, email, password_hash, is_active=False):
    return execute_returning(
        """
        INSERT INTO benutzer (name, email, passwort_hash, is_active)
        VALUES (%s, %s, %s, %s)
        RETURNING id
        """,
        (name, email, password_hash, is_active),
    )


def activate_user_by_email(email):
    execute(
        """
        UPDATE benutzer
        SET is_active = TRUE
        WHERE email = %s
        """,
        (email,),
    )


def update_user_password(user_id, password_hash):
    execute(
        """
        UPDATE benutzer
        SET passwort_hash = %s
        WHERE id = %s
        """,
        (password_hash, user_id),
    )


def update_user_last_login(user_id):
    execute(
        """
        UPDATE benutzer
        SET last_login = NOW()
        WHERE id = %s
        """,
        (user_id,),
    )


def log_access(user_id):
    execute(
        """
        INSERT INTO accesslog (user_id)
        VALUES (%s)
        """,
        (user_id,),
    )


def get_user_groups(user_id):
    rows = fetch_all(
        """
        SELECT g.gruppenname
        FROM benutzer_gruppen bg
        JOIN gruppen g ON g.id = bg.gruppen_id
        WHERE bg.benutzer_id = %s
        ORDER BY g.gruppenname
        """,
        (user_id,),
    )
    return [row["gruppenname"] for row in rows]


###############
# Themen
###############

def get_all_themen():
    return fetch_all(
        """
        SELECT id, kurztitel, titel, infotext, zusatztext, reihenfolge, pic
        FROM thema
        ORDER BY reihenfolge,id
        """
    )


def get_thema_by_id(thema_id):
    return fetch_one(
        """
        SELECT id, kurztitel, titel, infotext, zusatztext, reihenfolge, pic
        FROM thema
        WHERE id = %s
        """,
        (thema_id,),
    )


def get_next_thema_id(current_thema_id):
    row = fetch_one(
        """
        SELECT id
        FROM thema
        WHERE id > %s
        ORDER BY id
        LIMIT 1
        """,
        (current_thema_id,),
    )
    return row["id"] if row else None


def get_all_ansprechpartner():
    return fetch_all(
        """
        SELECT id, name, email, infotext
        FROM ansprechpartner
        ORDER BY name ASC
        """
    )


def get_all_contacts():
    return fetch_all(
        """
        SELECT id, name, email, infotext
        FROM ansprechpartner
        ORDER BY name ASC
        """
    )


def get_thema_ansprechpartner(thema_id):
    return fetch_all(
        """
        SELECT a.id, a.name, a.email, a.infotext
        FROM ansprechpartner a
        JOIN themaansprechpartner ta ON ta.ansprechpartner_id = a.id
        WHERE ta.thema_id = %s
        ORDER BY a.name ASC
        """,
        (thema_id,),
    )


def get_ansprechpartner_ids_for_thema(thema_id):
    rows = fetch_all(
        """
        SELECT ansprechpartner_id
        FROM themaansprechpartner
        WHERE thema_id = %s
        """,
        (thema_id,),
    )
    return [row["ansprechpartner_id"] for row in rows]


def create_thema(kurztitel, titel, infotext, zusatztext, reihenfolge, pic, ansprechpartner_ids, branche_ids=None):
    if branche_ids is None:
        branche_ids = []

    row = execute_returning(
        """
        INSERT INTO thema (kurztitel, titel, infotext, zusatztext, reihenfolge, pic)
        VALUES (%s, %s, %s, %s, %s, %s)
        RETURNING id
        """,
        (kurztitel, titel, infotext, zusatztext, reihenfolge, pic),
    )
    thema_id = row["id"]

    for ap_id in ansprechpartner_ids:
        execute(
            """
            INSERT INTO themaansprechpartner (thema_id, ansprechpartner_id)
            VALUES (%s, %s)
            """,
            (thema_id, ap_id),
        )

    for branche_id in branche_ids:
        execute(
            """
            INSERT INTO branchenthemen (branche_id, thema_id)
            VALUES (%s, %s)
            """,
            (branche_id, thema_id),
        )

    return thema_id


def update_thema(thema_id, kurztitel, titel, infotext, reihenfolge, zusatztext, pic, ansprechpartner_ids, branche_ids=None):
    if branche_ids is None:
        branche_ids = []

    execute(
        """
        UPDATE thema
        SET kurztitel = %s,
            titel = %s,
            infotext = %s,
            zusatztext = %s,
            reihenfolge = %s,
            pic = %s
        WHERE id = %s
        """,
        (kurztitel, titel, infotext, zusatztext, reihenfolge, pic, thema_id),
    )

    execute(
        """
        DELETE FROM themaansprechpartner
        WHERE thema_id = %s
        """,
        (thema_id,),
    )

    for ap_id in ansprechpartner_ids:
        execute(
            """
            INSERT INTO themaansprechpartner (thema_id, ansprechpartner_id)
            VALUES (%s, %s)
            """,
            (thema_id, ap_id),
        )

    execute(
        """
        DELETE FROM branchenthemen
        WHERE thema_id = %s
        """,
        (thema_id,),
    )

    for branche_id in branche_ids:
        execute(
            """
            INSERT INTO branchenthemen (branche_id, thema_id)
            VALUES (%s, %s)
            """,
            (branche_id, thema_id),
        )

def delete_thema(thema_id):
    execute("DELETE FROM themaansprechpartner WHERE thema_id = %s", (thema_id,))
    execute("DELETE FROM fragen WHERE thema_id = %s", (thema_id,))
    execute("DELETE FROM thema WHERE id = %s", (thema_id,))


###############
# Fragen
###############

def get_thema_questions(thema_id):
    return fetch_all(
        """
        SELECT id, thema_id, text
        FROM fragen
        WHERE thema_id = %s
        ORDER BY reihenfolge, id
        """,
        (thema_id,),
    )


def get_all_questions_with_thema():
    return fetch_all(
        """
        SELECT
            f.id,
            f.text,
            f.thema_id,
            t.id AS thema_sort_id,
            t.kurztitel,
            t.reihenfolge,
            t.titel,
            COALESCE(STRING_AGG(b.branchenname, ' | '), '') AS branchen
        FROM fragen f
        JOIN thema t
            ON t.id = f.thema_id
        LEFT JOIN branchenthemen bt
            ON bt.thema_id = t.id
        LEFT JOIN branche b
            ON b.id = bt.branche_id
        GROUP BY
            f.id,
            f.text,
            f.thema_id,
            t.id,
            t.kurztitel,
            t.titel
        ORDER BY t.reihenfolge, t.id, f.reihenfolge, f.id
        """
    )


###############
# Assessment
###############

def create_assessment(user_id):
    row = execute_returning(
        """
        INSERT INTO assessment (user_id)
        VALUES (%s)
        RETURNING id
        """,
        (user_id,),
    )
    return row["id"]


def save_assessment_answer(assessment_id, thema_id, frage_id, antwort):
    execute(
        """
        INSERT INTO assessmentanswer (assessmentid, thema_id, frage_id, antwort)
        VALUES (%s, %s, %s, %s)
        ON CONFLICT (assessmentid, frage_id)
        DO UPDATE SET antwort = EXCLUDED.antwort
        """,
        (assessment_id, thema_id, frage_id, antwort),
    )


def get_assessment_result_rows(assessment_id, branche_id):
    return fetch_all(
        """
        SELECT
            t.id,
            t.kurztitel,
            t.titel,
            COUNT(*) FILTER (WHERE aa.antwort = TRUE) AS ja_anzahl
        FROM thema t
        JOIN branchenthemen bt
            ON bt.thema_id = t.id
        LEFT JOIN assessmentanswer aa
            ON aa.thema_id = t.id
           AND aa.assessmentid = %s
        WHERE bt.branche_id = %s
        GROUP BY t.id, t.kurztitel, t.titel
        ORDER BY t.id
        """,
        (assessment_id, branche_id),
    )
############
# Ansprechpartner
############

def get_ansprechpartner_by_id(ansprechpartner_id):
    return fetch_one(
        """
        SELECT id, name, email, infotext
        FROM ansprechpartner
        WHERE id = %s
        """,
        (ansprechpartner_id,),
    )


def create_ansprechpartner(name, email, infotext):
    row = execute_returning(
        """
        INSERT INTO ansprechpartner (name, email, infotext)
        VALUES (%s, %s, %s)
        RETURNING id
        """,
        (name, email, infotext),
    )
    return row["id"]


def update_ansprechpartner(ansprechpartner_id, name, email, infotext):
    execute(
        """
        UPDATE ansprechpartner
        SET name = %s,
            email = %s,
            infotext = %s
        WHERE id = %s
        """,
        (name, email, infotext, ansprechpartner_id),
    )


def delete_ansprechpartner(ansprechpartner_id):
    execute(
        """
        DELETE FROM themaansprechpartner
        WHERE ansprechpartner_id = %s
        """,
        (ansprechpartner_id,),
    )
    execute(
        """
        DELETE FROM ansprechpartner
        WHERE id = %s
        """,
        (ansprechpartner_id,),
    )

###############
# Fragen
###############
def get_question_by_id(frage_id):
    return fetch_one(
        """
        SELECT id, thema_id, text, reihenfolge
        FROM fragen
        WHERE id = %s
        """,
        (frage_id,),
    )


def create_question(thema_id, text, reihenfolge):
    return execute_returning(
        """
        INSERT INTO fragen (thema_id, text, reihenfolge)
        VALUES (%s, %s, %s)
        RETURNING id
        """,
        (thema_id, text, reihenfolge),
    )


def update_question(question_id, thema_id, text, reihenfolge):
    execute(
        """
        UPDATE fragen
        SET thema_id = %s,
            text = %s,
            reihenfolge = %s
        WHERE id = %s
        """,
        (
            thema_id,
            text,
            reihenfolge,
            question_id,
        ),
    )


def delete_question(frage_id):
    execute(
        "DELETE FROM fragen WHERE id = %s",
        (frage_id,),
    )

##########
# usermanagement
##########
def activate_user(user_id):
    execute(
        """
        UPDATE benutzer
        SET is_active = TRUE
        WHERE id = %s
        """,
        (user_id,),
    )


def delete_user(user_id, current_user_id=None):
    if current_user_id is not None and int(user_id) == int(current_user_id):
        raise ValueError("Der aktuell angemeldete Benutzer kann nicht gelöscht werden.")
    execute(
        """
        DELETE FROM benutzer
        WHERE id = %s
        """,
        (user_id,),
    )

###############
# Branchen
###############

def get_all_branchen():
    return fetch_all(
        """
        SELECT id, branchenname
        FROM branche
        ORDER BY branchenname ASC
        """
    )


def get_branche_by_id(branche_id):
    return fetch_one(
        """
        SELECT id, branchenname
        FROM branche
        WHERE id = %s
        """,
        (branche_id,),
    )


def get_thema_ids_for_branche(branche_id):
    rows = fetch_all(
        """
        SELECT thema_id
        FROM branchenthemen
        WHERE branche_id = %s
        """,
        (branche_id,),
    )
    return [row["thema_id"] for row in rows]


def create_branche(branchenname, thema_ids):
    row = execute_returning(
        """
        INSERT INTO branche (branchenname)
        VALUES (%s)
        RETURNING id
        """,
        (branchenname,),
    )
    branche_id = row["id"]

    for thema_id in thema_ids:
        execute(
            """
            INSERT INTO branchenthemen (branche_id, thema_id)
            VALUES (%s, %s)
            """,
            (branche_id, thema_id),
        )

    return branche_id


def update_branche(branche_id, branchenname, thema_ids):
    execute(
        """
        UPDATE branche
        SET branchenname = %s
        WHERE id = %s
        """,
        (branchenname, branche_id),
    )

    execute(
        """
        DELETE FROM branchenthemen
        WHERE branche_id = %s
        """,
        (branche_id,),
    )

    for thema_id in thema_ids:
        execute(
            """
            INSERT INTO branchenthemen (branche_id, thema_id)
            VALUES (%s, %s)
            """,
            (branche_id, thema_id),
        )


def delete_branche(branche_id):
    execute(
        """
        DELETE FROM branchenthemen
        WHERE branche_id = %s
        """,
        (branche_id,),
    )
    execute(
        """
        DELETE FROM branche
        WHERE id = %s
        """,
        (branche_id,),
    )

def get_branche_ids_for_thema(thema_id):
    rows = fetch_all(
        """
        SELECT branche_id
        FROM branchenthemen
        WHERE thema_id = %s
        """,
        (thema_id,),
    )
    return [row["branche_id"] for row in rows]


def update_thema_branchen(thema_id, branche_ids):
    execute(
        """
        DELETE FROM branchenthemen
        WHERE thema_id = %s
        """,
        (thema_id,),
    )

    for branche_id in branche_ids:
        execute(
            """
            INSERT INTO branchenthemen (branche_id, thema_id)
            VALUES (%s, %s)
            """,
            (branche_id, thema_id),
        )

def get_all_branchen():
    return fetch_all(
        """
        SELECT id, branchenname
        FROM branche
        ORDER BY branchenname ASC
        """
    )

def get_themen_for_branche(branche_id):
    return fetch_all(
        """
        SELECT t.id, t.kurztitel, t.titel, t.infotext, t.zusatztext
        FROM thema t
        JOIN branchenthemen bt ON bt.thema_id = t.id
        WHERE bt.branche_id = %s
        ORDER BY t.id
        """,
        (branche_id,),
    )

def get_next_thema_id_for_branche(current_thema_id, branche_id):
    row = fetch_one(
        """
        SELECT t.id
        FROM thema t
        JOIN branchenthemen bt ON bt.thema_id = t.id
        WHERE bt.branche_id = %s
          AND t.id > %s
        ORDER BY t.id
        LIMIT 1
        """,
        (branche_id, current_thema_id),
    )
    return row["id"] if row else None

def get_thema_for_branche(thema_id, branche_id):
    return fetch_one(
        """
        SELECT t.id, t.kurztitel, t.titel, t.infotext, t.zusatztext, t.pic
        FROM thema t
        JOIN branchenthemen bt ON bt.thema_id = t.id
        WHERE t.id = %s
          AND bt.branche_id = %s
        """,
        (thema_id, branche_id),
    )

def get_question_count_for_thema(thema_id):
    row = fetch_one(
        """
        SELECT COUNT(*) AS anzahl
        FROM fragen
        WHERE thema_id = %s
        """,
        (thema_id,),
    )
    return int(row["anzahl"]) if row else 0


def get_all_themen_with_question_count():
    return fetch_all(
        """
        SELECT
            t.id,
            t.kurztitel,
            t.titel,
            t.infotext,
            t.zusatztext,
            t.reihenfolge,
            t.pic,
            COUNT(f.id) AS fragen_anzahl
        FROM thema t
        LEFT JOIN fragen f ON f.thema_id = t.id
        GROUP BY t.id, t.kurztitel, t.titel, t.infotext, t.zusatztext
        ORDER BY t.reihenfolge, t.id
        """
    )

def get_random_ansprechpartner_for_thema(thema_id):
    return fetch_one(
        """
        SELECT a.id, a.name, a.email, a.infotext
        FROM ansprechpartner a
        JOIN themaansprechpartner ta ON ta.ansprechpartner_id = a.id
        WHERE ta.thema_id = %s
        ORDER BY RANDOM()
        LIMIT 1
        """,
        (thema_id,),
    )

def create_empfehlung(user_id, thema_id, ansprechpartner_id):
    return execute_returning(
        """
        INSERT INTO empfehlung (user_id, thema_id, ansprechpartner_id)
        VALUES (%s, %s, %s)
        RETURNING id
        """,
        (user_id, thema_id, ansprechpartner_id),
    )

def get_pdf_recommendation_topics(assessment_id, branche_id):
    return fetch_all(
        """
        SELECT
            t.id,
            t.titel,
            t.zusatztext,
            COUNT(*) FILTER (WHERE aa.antwort = TRUE) AS ja_anzahl
        FROM thema t
        JOIN branchenthemen bt
            ON bt.thema_id = t.id
        LEFT JOIN assessmentanswer aa
            ON aa.thema_id = t.id
           AND aa.assessmentid = %s
        WHERE bt.branche_id = %s
        GROUP BY t.id, t.titel, t.zusatztext
        HAVING COUNT(*) FILTER (WHERE aa.antwort = TRUE) < 7
        ORDER BY t.id
        """,
        (assessment_id, branche_id),
    )

def get_all_empfehlungen():
    return fetch_all(
        """
        SELECT
            e.id,
            e.datetime,
            a.id AS ansprechpartner_id,
            a.name AS ansprechpartner_name,
            t.id AS thema_id,
            t.titel AS thema_titel,
            b.id AS user_id,
            b.name AS user_name
        FROM empfehlung e
        JOIN ansprechpartner a
            ON a.id = e.ansprechpartner_id
        JOIN thema t
            ON t.id = e.thema_id
        JOIN benutzer b
            ON b.id = e.user_id
        ORDER BY a.name ASC, e.datetime DESC
        """
    )