from flask import Blueprint, request, redirect, url_for, render_template, current_app
from urllib.parse import urlencode, parse_qs, unquote_plus
from flask_login import current_user
from extensions import db
from jinja2_fragments import render_block

tags_bp = Blueprint('tags', __name__, url_prefix='/tag')

# Helper function to get tags (assuming similar logic exists in dashboard/person_overview)
# NOTE: This is a placeholder and might need adjustment based on actual data fetching logic
def _get_tags_data(person_id=None):
    if person_id:
        # Logic to fetch tags for a specific person
        tags_raw = db.get_tags_for_person(person_id)
        # Assuming get_tags_for_person returns list like [{'tag_id': 1, 'tag_name': 'Bulk', 'tag_filter': '?tag=Bulk'}]
        return tags_raw # Adjust based on actual return format
    else:
        tags_raw = db.get_tags_for_dashboard()
        return tags_raw


@tags_bp.route("/redirect", methods=['GET'])
def goto_tag():
    """Redirects or loads content based on tag filter."""
    person_id = request.args.get("person_id")
    tag_filter = request.args.get("filter", "") # Default to empty string

    if person_id:
        # Assuming person_overview handles HTMX requests to render blocks
        # Corrected endpoint name for person overview
        target_url = url_for('person_overview', person_id=person_id) + tag_filter
        # Check if it's an HTMX request targeting #container
        if request.headers.get('HX-Target') == 'container' or request.headers.get('HX-Target') == '#container':
             # Need the actual function that renders person_overview content block
             # Placeholder: Re-render the person overview block with the filter
             # This requires knowing how person_overview fetches its data based on filters
             # return render_block('person_overview.html', 'content_block', person_id=person_id, filter=tag_filter)
             # For now, let's assume a full redirect might be simpler if block rendering is complex
             return redirect(target_url)
        else:
            return redirect(target_url)
    else:
        # Assuming dashboard handles HTMX requests to render blocks
        target_url = url_for('dashboard') + tag_filter
        if request.headers.get('HX-Target') == 'container' or request.headers.get('HX-Target') == '#container':
             # Need the actual function that renders dashboard content block
             # Placeholder: Re-render the dashboard block with the filter
             # This requires knowing how dashboard fetches its data based on filters
             # return render_block('dashboard.html', 'content_block', filter=tag_filter)
             # For now, let's assume a full redirect might be simpler
             return redirect(target_url)
        else:
            return redirect(target_url)


@tags_bp.route("/add", methods=['POST']) # Changed to POST
def add_tag():
    """Adds a tag and returns the updated tags partial."""
    person_id = request.form.get("person_id") # Get from form data
    tag_name = request.form.get('tag_name')
    current_filter_str = request.form.get('current_filter', '')

    if not tag_name:
        # Handle error - maybe return an error message partial?
        # For now, just re-render tags without adding
        tags = _get_tags_data(person_id)
        return render_template('partials/tags.html', tags=tags, person_id=person_id)


    # Parse the current filter string, add the new tag, and re-encode
    parsed_params = parse_qs(current_filter_str)
    # parse_qs returns lists for values, handle potential existing 'tag' param
    parsed_params['tag'] = [tag_name] # Set/overwrite tag param with the new one
    # Re-encode, ensuring proper handling of multiple values if needed (though 'tag' is likely single)
    tag_filter_value = "?" + urlencode(parsed_params, doseq=True)

    if person_id:
        db.add_or_update_tag_for_person(person_id, tag_name, tag_filter_value)
    else:
        db.add_or_update_tag_for_dashboard(tag_name, tag_filter_value)

    # Fetch updated tags and render the partial
    tags = _get_tags_data(person_id)
    return render_template('partials/tags.html', tags=tags, person_id=person_id)


@tags_bp.route("/<int:tag_id>/delete", methods=['DELETE']) # Changed to DELETE
def delete_tag(tag_id):
    """Deletes a tag and returns the updated tags partial."""
    # We might get person_id from request body/headers if needed, or assume context
    # For simplicity, let's try deleting based on tag_id only first, assuming tags are unique enough or context is handled elsewhere
    # If person_id is strictly required for deletion scope:
    person_id = request.form.get("person_id") # Or from headers/session

    # Decide which delete function to call based on context (person page vs dashboard)
    if person_id:
        db.delete_tag_for_person(person_id=person_id, tag_id=tag_id)
    else:
        db.delete_tag_for_dashboard(tag_id=tag_id)

    # Fetch updated tags and render the partial
    tags = _get_tags_data(person_id)
    return render_template('partials/tags.html', tags=tags, person_id=person_id)

# --- Workout Specific Tag Routes ---

@tags_bp.route("/workout/<int:workout_id>/add", methods=['POST'])
def add_tag_to_workout(workout_id):
    """Adds existing tags to a specific workout."""
    # Note: Authorization (checking if the current user can modify this workout) might be needed here.
    tags_id = [int(i) for i in request.form.getlist('tag_id')]

    # --- Start: DB logic from db.add_tag_for_workout ---
    # If tags_id is not empty, delete tags that are not in the new selection
    if tags_id:
        db.execute(
            """
            DELETE FROM workout_tag
            WHERE workout_id = %s AND tag_id NOT IN %s
            """,
            [workout_id, tuple(tags_id)], commit=True
        )
    else:
        # If tags_id is empty, delete all tags for this workout
        db.execute(
            """
            DELETE FROM workout_tag
            WHERE workout_id = %s
            """,
            [workout_id], commit=True
        )

    # Then, attempt to insert the new tags
    for tag_id in tags_id:
        db.execute(
            """
            INSERT INTO workout_tag (workout_id, tag_id)
            VALUES (%s, %s)
            ON CONFLICT (workout_id, tag_id) DO NOTHING
            """,
            [workout_id, tag_id], commit=True
        )

    # Now fetch updated list of workout tags
    workout_tags = db.execute("""
        SELECT
            T.tag_id AS "tag_id",
            T.person_id AS "person_id",
            T.name AS "tag_name",
            T.filter AS "tag_filter",
            TRUE AS "is_selected" -- Mark these as selected since they are now associated
        FROM Workout_Tag WT
            LEFT JOIN Tag T ON WT.tag_id=T.tag_id
        WHERE WT.workout_id=%s
        ORDER BY T.name ASC -- Keep consistent ordering
        """, [workout_id])
    # --- End: DB logic from db.add_tag_for_workout ---

    # We need person_id for rendering the partial if it requires it, fetch it.
    workout_info = db.execute("SELECT person_id FROM workout WHERE workout_id = %s", [workout_id], one=True)
    person_id = workout_info['person_id'] if workout_info else None

    # The partial likely needs the full tag list for the workout, including unselected ones.
    # Let's fetch all relevant tags for the person and mark selected ones.
    all_person_tags = db.execute("""
        SELECT
            tag.tag_id,
            tag.name AS tag_name,
            tag.filter as tag_filter,
            EXISTS (
                SELECT 1 FROM workout_tag wt_check
                WHERE wt_check.workout_id = %s AND wt_check.tag_id = tag.tag_id
            ) AS is_selected
        FROM tag
        WHERE tag.person_id = %s OR tag.person_id IS NULL -- Include global tags
        ORDER BY tag.name ASC;
        """, [workout_id, person_id])


    # Render the partial with the complete list of tags (selected and unselected)
    return render_template('partials/workout_tags_list.html', tags=all_person_tags, person_id=person_id, workout_id=workout_id)

@tags_bp.route("/workout/<int:workout_id>/new", methods=['POST'])
def create_new_tag_for_workout(workout_id):
    """Creates a new tag and associates it with a specific workout."""
    # Note: Authorization might be needed here.
    tag_name = request.form.get('tag_name')
    if not tag_name:
        # Handle error: Tag name cannot be empty
        # Consider returning an error message via HTMX
        return "Tag name cannot be empty", 400

    # Fetch person_id associated with the workout_id first
    workout_info = db.execute("SELECT person_id FROM workout WHERE workout_id = %s", [workout_id], one=True)
    if not workout_info:
        # Handle error: Workout not found
        return "Workout not found", 404
    person_id = workout_info['person_id']

    # --- Start: DB logic from db.create_tag_for_workout ---
    # Determine tag filter based on exercises in the workout (if any)
    workout_exercises = db.execute("""
        SELECT DISTINCT E.exercise_id AS "exercise_id"
        FROM Workout W
            LEFT JOIN TopSet T ON W.workout_id=T.workout_id
            LEFT JOIN Exercise E ON T.exercise_id=E.exercise_id
        WHERE W.workout_id=%s AND E.exercise_id IS NOT NULL""", [workout_id])

    tag_filter = ""
    if workout_exercises:
        tag_filter = "?" + "&".join(
            f"exercise_id={e['exercise_id']}" for e in workout_exercises)

    # Create tag for person (or update if exists with same name for this person)
    # Check if tag already exists for this person
    existing_tag = db.execute(
        "SELECT tag_id FROM Tag WHERE person_id = %s AND name = %s",
        [person_id, tag_name], one=True
    )

    new_tag_id = None
    if existing_tag:
        new_tag_id = existing_tag['tag_id']
        # Optionally update the filter if it's different? For now, just use existing tag.
        # db.execute("UPDATE Tag SET filter = %s WHERE tag_id = %s", [tag_filter, new_tag_id], commit=True)
    else:
        # Create the new tag
        row = db.execute(
            'INSERT INTO Tag (person_id, name, filter) VALUES (%s, %s, %s) RETURNING tag_id AS "tag_id"',
            [person_id, tag_name, tag_filter], commit=True, one=True
        )
        if row:
            new_tag_id = row['tag_id']

    if not new_tag_id:
        # Handle error: Failed to create or find tag
        return "Error creating tag", 500

    # Add tag to workout (handle conflict just in case)
    db.execute(
        'INSERT INTO Workout_Tag (workout_id, tag_id) VALUES (%s, %s) ON CONFLICT (workout_id, tag_id) DO NOTHING',
        [workout_id, new_tag_id], commit=True
    )

    # Now fetch updated list of all workout tags (selected and unselected)
    all_person_tags = db.execute("""
        SELECT
            tag.tag_id,
            tag.name AS tag_name,
            tag.filter as tag_filter,
            EXISTS (
                SELECT 1 FROM workout_tag wt_check
                WHERE wt_check.workout_id = %s AND wt_check.tag_id = tag.tag_id
            ) AS is_selected
        FROM tag
        WHERE tag.person_id = %s OR tag.person_id IS NULL -- Include global tags
        ORDER BY tag.name ASC;
        """, [workout_id, person_id])
    # --- End: DB logic from db.create_tag_for_workout ---

    # Render the partial with the complete list of tags
    # Use 'tags' as the variable name consistent with the other endpoint and likely the partial
    return render_template('partials/workout_tags_list.html', tags=all_person_tags, person_id=person_id, workout_id=workout_id)