"""Views for the main app."""

import json
from functools import reduce

from django.shortcuts import render, redirect
from django.http import JsonResponse
from django.db.models import Q, Case, When, Value, IntegerField, Min
from django.db.utils import IntegrityError
from django.views import View
from django.views.decorators.http import require_GET, require_POST
from django.http import HttpRequest, HttpResponseNotFound
from django.core import serializers
from django.forms.models import model_to_dict

from .models import Venue, Waters
# from .models import Team, Member, Section, SectionManager, ReusableAutoField, SectionValidator


def index(request):
    return render(request, 'index.html')

def results(request):
    return render(request, 'results.html')

def scoreboard(request):
    return render(request, 'scoreboard.html')

def teams(request):
    return render(request, 'teams.html')

def venues(request):

    venues = Venue.objects.all()
    context = {"venues": venues, "venue_types": Venue.VENUE_TYPES}

    return render(request, 'venues.html', context)

def venue_details(request, venue_id):

    try:
        venue = Venue.objects.get(id=venue_id)
    except Venue.DoesNotExist:
        return HttpResponseNotFound("<h1>404 - Venue not found</h1>")

    context = {"venue": venue}

    return render(request, 'venue_details.html', context)

def get_venue_details(request, venue_id: int):

    try:
        venue = Venue.objects.get(pk=venue_id)
    except Venue.DoesNotExist:
        return JsonResponse({"error": "Venue not found"}, status=404)

    json_venue = model_to_dict(venue)

    return JsonResponse({"data": json_venue})

def get_venue_waters(request, venue_id: int):

    try:
        venue = Venue.objects.get(pk=venue_id)
    except Venue.DoesNotExist:
        return JsonResponse({"error": "Venue not found"}, status=404)

    waters = Waters.objects.filter(venue=venue)
    waters_data = [
        {
            "name": water.name,
            "description": water.description,
            "pegs_min": water.pegs_min,
            "pegs_max": water.pegs_max,
            "water_type": water.water_type,
            "fish_type": water.fish_type
        }
        for water in waters
    ]
    return JsonResponse({"waters", waters_data})

# def bulk_create_pegs(request):
#     """Bulk create pegs"""

#     number_of_pegs = request.POST.get("pegAmount")
#     for i in range(int(number_of_pegs)):
#         Peg.objects.create()

#     # return to previous page
#     return redirect(request.META.get('HTTP_REFERER'))

def name_sort_key(section):
    if len(section.name) == 1:
        return (0, section.name)
    else:
        return (1, section.name[:-1], section.name[-1])


class ManageAnglersView(View):
    """View for the Manage Anglers page."""

    template_name = "anglers.html"

    def get(self, request: HttpRequest, *args, **kwargs) -> HttpRequest:
        """Handle GET requests to the Manage Anglers page.

        Args:
            request (HttpRequest): The HttpRequest object, contains GET data.

        Returns:
            HttpRequest: A render of the Manage Anglers page.
        """

        anglers = Member.objects.order_by("first_name", "last_name")
        context = {"anglers": anglers}

        return render(request, self.template_name, context)

    def post(self, request: HttpRequest, *args, **kwargs) -> JsonResponse:
        """Handle POST requests to the Manage Anglers page.

        Args:
            request (HttpRequest): The HttpRequest object, contains POST data.

        Returns:
            JsonResponse: Contains the result of the action.
        """

        tasks = request.POST.getlist("tasks[]")

        data = {}
        for task in tasks:
            data.update(self.handle_task(request, task))

        return JsonResponse(data)

    def handle_task(self, request, task: str) -> dict[str, str]:
        """Handle a task.

        Args:
            request (HttpRequest): HttpRequest object, contains POST data.
            task (str): The task to handle.

        Raises:
            ValueError: The task is invalid.

        Returns:
            dict[str, str]: The result of the task.
        """

        # Format is {key = ACTION-TASK_NAME: value = HANDLER_FUNCTION}
        task_handlers = {
            "update-team": self.update_team,
            "update-section": self.update_section,
            "update-angler": self.update_angler,
            "get-teams": self.get_teams,
            "get-sections": self.get_sections,
            "get-anglers": self.get_anglers,
            "delete-team": self.delete_team,
            "delete-section": self.delete_section,
            "delete-angler": self.delete_angler,
            "get-nextTeamNumber": self.get_next_team_number,
            "get-nextPegNumber": self.get_next_peg_number,
        }

        handler = task_handlers.get(task)
        if not handler:
            raise ValueError(f"Invalid task: {task}")

        return handler(request)

    def update_team(self, request) -> dict[str]:
        """Update a team, returns a dictionary of the new team's data."""

        result = {"form_errors": {}, "team": None}
        team_id = request.POST.get("id")
        team_number = request.POST.get("number")

        if not (team_id and team_number):
            raise ValueError("Team ID or Team Number is missing or empty")

        if team_id == "-1":
            team = Team(number=team_number)
        else:
            team = Team.objects.get(id=team_id)
            team.number = team_number

        try:
            team.save()
            result["team"] = {"id": team.id, "number": team.number}
        except IntegrityError:
            result["form_errors"]["#teamNumber"] = "A Team with this number already exists"

        return result

    def update_section(self, request) -> dict[str]:
        """Update a section, returns a dictionary of the new section's data."""

        result = {"form_errors": {}, "section": None}
        section_id = request.POST.get("id")
        section_character = request.POST.get("character")

        if not (section_id and section_character):
            raise ValueError("Section ID or Section Character is missing or empty")

        if section_id == "-1":
            section = Section(character=section_character)
        else:
            section = Section.objects.get(id=section_id)
            section.character = section_character

        try:
            section.save()
            result["section"] = {"id": section.id, "character": section.character}
        except IntegrityError:
            result["form_errors"]["#editSectionNameError"] = "A Section with this character already exists"

        return result

    def update_angler(self, request) -> dict[str]:
        """Update an Angler, returns a dictionary of the new angler's data."""

        result = {"form_errors": {}, "angler": None}
        angler_id = request.POST.get("angler_id")
        forename = request.POST.get("forename")
        surname = request.POST.get("surname")
        peg_number = request.POST.get("peg_number")
        team_id = request.POST.get("team_id")
        section_id = request.POST.get("section_id")

        if not angler_id:
            raise ValueError("Invalid angler ID")

        team = Team.objects.get(id=team_id)
        section = Section.objects.get(id=section_id)

        if angler_id == "-1":
            angler = Member(
                first_name=forename,
                last_name=surname,
                peg_number=peg_number,
                team=team,
                section=section
            )
        else:
            angler = Member.objects.get(id=angler_id)
            angler.first_name = forename
            angler.last_name = surname
            angler.peg_number = peg_number
            angler.team = team
            angler.section = section

        try:
            angler.save()
        except IntegrityError:
            result["form_errors"]["#anglerPeg"] = "An Angler with this peg number already exists"

        result["angler"] = {
            "id": angler.id,
            "forename": forename,
            "surname": surname,
            "peg_number": peg_number,
            "team_id": team_id,
            "section_id": section_id,
            "team_number": angler.team.number,
            "section_character": angler.section.character
        }

        return result

    def get_teams(self, request) -> dict[str]:
        """Returns a dictionary of all teams."""

        search = request.POST.get("search")
        teams = Team.objects.order_by("number").all()

        # Search works by exluding teams that do not contain members with the search term in their names.
        if search:
            search_terms = search.split()
            members = Member.objects.filter(reduce(lambda x, y: x & y, [
                Q(first_name__icontains=term) | Q(last_name__icontains=term)
                for term in search_terms
            ]))
            teams = teams.filter(members__in=members).distinct()

        return {"teams": [{"id": team.id, "number": team.number} for team in teams]}

    def get_sections(self, request) -> dict[str]:
        """Returns a dictionary of all sections."""

        search = request.POST.get("search")
        sections = Section.objects.order_by("character").all()

        if search:
            search_terms = search.split()
            members = Member.objects.filter(reduce(lambda x, y: x & y, [
                Q(first_name__icontains=term) | Q(last_name__icontains=term)
                for term in search_terms
            ]))
            sections = sections.filter(members__in=members).distinct()

        return {"sections": [{"id": section.id, "character": section.character} for section in sections]}

    def get_anglers(self, request) -> dict[str]:
        """Returns a dictionary of all anglers."""

        search = request.POST.get("search")
        anglers = Member.objects.order_by("first_name").all()
        order_by = "peg_number" if request.POST.get("sortAnglers") == "pegs" else "first_name"

        if search:
            search_terms = search.split()
            anglers = anglers.filter(reduce(lambda x, y: x & y, [
                Q(first_name__icontains=term) | Q(last_name__icontains=term)
                for term in search_terms
            ])).distinct()

        return {
            "anglers": [
                {
                    "id": angler.id,
                    "first_name": angler.first_name,
                    "last_name": angler.last_name,
                    "peg_number": angler.peg_number,
                    "team_id": angler.team.id,
                    "section_id": angler.section.id,
                    "team_number": angler.team.number,
                    "section_character": angler.section.character
                }
                for angler in anglers.order_by(order_by).all()
            ]
        }

    def delete_team(self, request) -> dict:
        """Deletes a team."""

        team_id = request.POST.get("team_id")
        if not team_id:
            raise ValueError("Invalid team ID")

        teams = Team.objects.get(id=team_id)
        teams.delete()

        return {}

    def delete_section(self, request) -> dict:
        """Deletes a section."""

        section_id = request.POST.get("section_id")
        if not section_id:
            raise ValueError("Invalid section ID")

        sections = Section.objects.get(id=section_id)
        sections.delete()

        return {}

    def delete_angler(self, request) -> dict:
        """Delete an angler."""

        angler_id = request.POST.get("angler_id")
        if not angler_id:
            raise ValueError("Invalid angler ID")

        angler = Member.objects.get(id=angler_id)
        angler.delete()

        return {}

    def get_next_team_number(self, request) -> dict[str, int]:
        """Returns the next available team number."""

        next_team_number = 1
        
        while Team.objects.filter(number=next_team_number).exists():
            next_team_number += 1

        return {"nextTeamNumber": next_team_number}

    def get_next_peg_number(self, request) -> dict[str, int]:
        """Returns the next available peg number."""

        next_peg_number = 1

        while Member.objects.filter(peg_number=next_peg_number).exists():
            next_peg_number += 1

        return {"nextPegNumber": next_peg_number}
















def get_angler_page_data(request, **kwargs):
    """Returns a JsonResponse containing a dictionary with a k/v pair for a list of teams.

    Args:
        request: the web request object.
    Returns:
        JsonResponse: dictionary of teams like so {'teams': [{}, {}, {}]}.
    """

    if not request.POST:
        return

    search = request.POST.get("search")
    sort_groups = request.POST.get("sortGroups") or "team"
    sort_members = request.POST.get("sortMembers") or "peg_number"

    teams = Team.objects.order_by("number").all()
    sections = Section.objects.order_by("character").all()

    if search:
        search_terms = search.split()
        members = Member.objects.filter(
            reduce(
                lambda x, y: x & y,  ## changed to AND from OR to fix bug with whitespace searches
                [
                    Q(first_name__icontains=term) | Q(last_name__icontains=term)
                    for term in search_terms
                ]
            )
        )
        teams = teams.filter(members__in=members).distinct()
        sections = sections.filter(members__in=members).distinct()

    response_data = {
        "teams": [
            {"id": team.id, "number": team.number}
            for team in teams
        ],
        "sections":  [
            {"id": sec.id, "character": sec.character}
            for sec in sections
        ],
        "anglers": [
            {
                "id": member.id,
                "first": member.first_name,
                "last": member.last_name,
                "peg": member.peg_number,
                "team_id": member.team.id if member.team else None,
                "section_id": member.section.id if member.section else None
            }
            for member in Member.objects.order_by(sort_members).all()
        ]
    }

    response_data["sortGroups"] = sort_groups
    response_data["sortMembers"] = sort_members

    for key, value in kwargs.items():
        response_data[key] = value

    return JsonResponse(response_data)





def update_member(request):
    """Update a member. Returns a JsonResponse with the updated teams."""

    if not request.POST:
        return

    # Get the updated values
    member_id = request.POST.get("memberId")
    first = request.POST.get("first")
    last = request.POST.get("last")
    team_number = request.POST.get("teamNumber")
    peg_number = request.POST.get("pegNumber")

    # Get the member and team
    member = Member.objects.get(id=member_id)
    team = Team.objects.get(name=team_number)

    # Update the member
    member.first_name = first
    member.last_name = last
    member.team = team
    member.peg_number = peg_number

    member.save()

    return get_angler_page_data(request)

def update_section(request):
    """Update a section, returns JsonResponse with updated teams data."""

    if not request.POST:
        return

    section_id = request.POST.get("sectionId")
    section_name = request.POST.get("sectionName")

    validator = SectionValidator()
    if not validator.is_valid(section_name):
        json_response = get_angler_page_data(request, form_errors={
            "editSectionName": "This is an invalid section"
        })
        return json_response

    if section_id == "-1":
        section = Section(character=section_name)
    else:
        section = Section.objects.get(id=section_id)
        section.character = section_name

    try:
        section.save()
    except IntegrityError:
        json_response = get_angler_page_data(request, form_errors={
            "editSectionName": "A Section with this character already exists"
        })
        return json_response

    return get_angler_page_data(request)  # returns jsonresponse with new details

def update_team(request):
    """Update a team, returns a JsonResponse with updated teams data."""

    if not request.POST:
        return

    team_id = request.POST.get("id")
    team_number = request.POST.get("number")

    try:
        if team_id == "-1":
            team = Team.objects.create(number=team_number)
        else:
            team = Team.objects.get(id=team_id)
            team.number = team_number
            team.save()
    except IntegrityError as error:
        json_response = get_angler_page_data(request, form_errors={
            "editTeamNumber": "A Team with this number already exists"
        })
        return json_response

    return get_angler_page_data(request)

def get_next_peg() -> int:
    pass

def get_next_section() -> str:

    section_name = SectionManager.get_max_section()
    return SectionManager.find_next_section(section_name)


def get_next_team() -> int:

    field = ReusableAutoField
    field.model = Team

    return field().get_default()


def get_next_identifier(request):
    """Get the next available identifer (peg, section character, etc.) for an object."""

    if not request.POST:
        return

    item = request.POST.get("item")

    match item:
        case "member_peg":
            result = get_next_peg()

        case "section_name":
            result = get_next_section()

        case "team_number":
            result = get_next_team()

        case _:
            raise ValueError(f"Bad identifier item: {item}")

    return JsonResponse({"identifier": result})
