import os

import requests
from django.conf import settings
from django.core.paginator import Paginator
from django.db import models
from django.db.models.functions import Lower
from django.http import HttpResponse, JsonResponse
from django.shortcuts import get_object_or_404, render
from django_downloadview import ObjectDownloadView
from django_http_exceptions import HTTPExceptions
from guardian.shortcuts import get_objects_for_user

from .models import Contract, ContractFile


class ContractFileDownloadView(ObjectDownloadView):
    model = ContractFile
    file_field = "file"
    attachment = False

    def get_queryset(self, *args, **kwargs):
        queryset = super().get_queryset(*args, **kwargs)

        if not self.current_user.has_perm("contracts.view_confidential"):
            queryset = queryset.filter(is_public=True)

        return queryset

    def get(self, request, *args, **kwargs):
        self.current_user = request.user

        return super().get(request, *args, **kwargs)


def get_base_context(request) -> dict:
    return {
        "site_url": settings.SITE_URL,
        "user": request.user,
    }


def get_pagination(request, objects) -> tuple:
    paginator = Paginator(objects, 25)

    page = paginator.get_page(request.GET.get("page"))

    return page, paginator


def get_paginated_contracts(request, filter=None, annotations=None) -> tuple:
    if filter is None:
        filter = models.Q()

    filter = filter & models.Q(status=Contract.StatusTypes.APPROVED)

    if not request.user.has_perm("contracts.view_confidential"):
        additional_filter = models.Q(is_public=True)

        if not request.user.is_anonymous:
            additional_filter = additional_filter | models.Q(created_by=request.user)

        filter = filter & additional_filter

    contracts = get_objects_for_user(request.user, "contracts.view_contract")

    if annotations is not None:
        contracts = contracts.annotate(**annotations)

    contracts = contracts.filter(filter).order_by("-valid_start_date").all()

    page, paginator = get_pagination(request, contracts)

    return page, paginator


def index(request):
    page, paginator = get_paginated_contracts(request)

    return render(
        request,
        "contracts/index.html",
        {
            **get_base_context(request),
            "title": "Seznam smluv",
            "description": "",
            "page": page,
            "paginator": paginator,
            "settings": Contract.settings,
        },
    )


def view_contract(request, id: int):
    filter = models.Q(status=Contract.StatusTypes.APPROVED)

    if not request.user.has_perm("contracts.view_confidential"):
        filter = filter & (
            models.Q(is_public=True)
            | (
                models.Q(created_by=request.user)
                if not request.user.is_anonymous
                else models.Value(True)
            )
        )

    contract = get_object_or_404(
        (get_objects_for_user(request.user, "contracts.view_contract").filter(filter)),
        id=id,
    )

    return render(
        request,
        "contracts/view_contract.html",
        {
            **get_base_context(request),
            "title": contract.name,
            "description": contract.summary,
            "contract": contract,
        },
    )


def search(request):
    original_query = request.GET.get("q", default="")
    query = original_query.lower()

    if query.isnumeric():
        number_query = int(query)
    else:
        number_query = 0

    page = paginator = None
    title = "Vyhledávání"

    if query:
        filter = models.Q(
            models.Q(lower_name__contains=query)
            | models.Q(lower_summary__contains=query)
            | models.Q(
                models.Q(contractee_signatures__contractee__ico_number=number_query)
                | models.Q(signee_signatures__signee__ico_number=number_query)
            )
            | models.Q(
                models.Q(contractee_signatures__contractee__name_lower__contains=query)
                | models.Q(signee_signatures__signee__name_lower__contains=query)
            )
            | models.Q(
                models.Q(
                    contractee_signatures__representatives__name_lower__contains=query
                )
                | models.Q(
                    signee_signatures__representatives__name_lower__contains=query
                )
            )
        )

        annotations = {
            "lower_name": Lower("name"),
            "lower_summary": Lower("summary"),
            "contractee_signatures__contractee__name_lower": Lower(
                "contractee_signatures__contractee__name"
            ),
            "signee_signatures__signee__name_lower": Lower(
                "signee_signatures__signee__name"
            ),
            "contractee_signatures__representatives__name_lower": Lower(
                "contractee_signatures__representatives__name"
            ),
            "signee_signatures__representatives__name_lower": Lower(
                "signee_signatures__representatives__name"
            ),
        }

        # WARNING: PostgreSQL-dependent
        page, paginator = get_paginated_contracts(
            request,
            filter,
            annotations,
        )

    return render(
        request,
        "contracts/search.html",
        {
            **get_base_context(request),
            "title": title,
            "description": "",
            "page": page,
            "paginator": paginator,
            "query": original_query,
        },
    )


# BEGIN Filtered contract + submodel views


def view_contract_filing_area(request, id: int):
    filing_area = get_object_or_404(
        get_objects_for_user(request.user, "contracts.view_contractfilingarea"), id=id
    )

    contracts_page, contracts_paginator = get_paginated_contracts(
        request, models.Q(filing_area=filing_area)
    )

    return render(
        request,
        "contracts/view_contract_filing_area.html",
        {
            **get_base_context(request),
            "title": filing_area.name,
            "description": (
                f"Spisovna smluv - {filing_area.name}, "
                f"zodpovědná osoba {filing_area.person_responsible}"
            ),
            "filing_area": filing_area,
            "contracts_page": contracts_page,
            "contracts_paginator": contracts_paginator,
        },
    )


def view_contract_issue(request, id: int):
    issue = get_object_or_404(
        get_objects_for_user(request.user, "contracts.view_contractissue"), id=id
    )

    contracts_page, contracts_paginator = get_paginated_contracts(
        request, models.Q(issues=issue)
    )

    return render(
        request,
        "contracts/view_contract_issue.html",
        {
            **get_base_context(request),
            "title": issue.name,
            "description": f"Problém se smlouvami - {issue.name}",
            "issue": issue,
            "contracts_page": contracts_page,
            "contracts_paginator": contracts_paginator,
        },
    )


def view_contract_type(request, id: int):
    type_ = get_object_or_404(
        get_objects_for_user(request.user, "contracts.view_contracttype"), id=id
    )

    contracts_page, contracts_paginator = get_paginated_contracts(
        request, models.Q(types=type_)
    )

    return render(
        request,
        "contracts/view_contract_type.html",
        {
            **get_base_context(request),
            "title": type_.name,
            "description": f"Typ smluv - {type_.name}",
            "type": type_,
            "contracts_page": contracts_page,
            "contracts_paginator": contracts_paginator,
        },
    )


def view_contractee(request, id: int):
    contractee = get_object_or_404(
        get_objects_for_user(request.user, "contracts.view_contractee"), id=id
    )

    contracts_page, contracts_paginator = get_paginated_contracts(
        request, models.Q(contractee_signatures__contractee=contractee)
    )

    return render(
        request,
        "contracts/view_contractee.html",
        {
            **get_base_context(request),
            "title": contractee.name,
            "description": f"Naše smluvní strana - {contractee.name}",
            "contractee": contractee,
            "contracts_page": contracts_page,
            "contracts_paginator": contracts_paginator,
        },
    )


def view_signee(request, id: int):
    signee = get_object_or_404(
        get_objects_for_user(request.user, "contracts.view_signee"), id=id
    )

    contracts_page, contracts_paginator = get_paginated_contracts(
        request, models.Q(signee_signatures__signee=signee)
    )

    return render(
        request,
        "contracts/view_signee.html",
        {
            **get_base_context(request),
            "title": signee.name,
            "description": f"Jiná smluvní strana - {signee.name}",
            "signee": signee,
            "contracts_page": contracts_page,
            "contracts_paginator": contracts_paginator,
        },
    )


# END Filtered contract + submodel views

# BEGIN Submodel listing views


def view_contract_filing_areas(request):
    filing_areas = get_objects_for_user(
        request.user,
        "contracts.view_contractfilingarea",
    ).order_by("name")

    page, paginator = get_pagination(request, filing_areas)

    return render(
        request,
        "contracts/view_contract_filing_areas.html",
        {
            **get_base_context(request),
            "title": "Spisovny",
            "description": "Seznam fyzických spisoven, kde jsou ukládány smlouvy.",
            "page": page,
            "paginator": paginator,
        },
    )


def view_contract_issues(request):
    issues = get_objects_for_user(
        request.user,
        "contracts.view_contractissue",
    ).order_by("name")

    page, paginator = get_pagination(request, issues)

    return render(
        request,
        "contracts/view_contract_issues.html",
        {
            **get_base_context(request),
            "title": (
                "Poznámky"
                if (request.user.is_anonymous or not request.user.can_view_confidential)
                else "Problémy"
            ),
            "description": (
                "Poznámky ke smlouvám."
                if (request.user.is_anonymous or not request.user.can_view_confidential)
                else "Problémy se smlouvami."
            ),
            "page": page,
            "paginator": paginator,
        },
    )


def view_contract_types(request):
    types = get_objects_for_user(
        request.user,
        "contracts.view_contracttype",
    ).order_by("name")

    page, paginator = get_pagination(request, types)

    return render(
        request,
        "contracts/view_contract_types.html",
        {
            **get_base_context(request),
            "title": "Typy",
            "description": "Typy smluv.",
            "page": page,
            "paginator": paginator,
        },
    )


def view_contractees(request):
    contractees = get_objects_for_user(
        request.user,
        "contracts.view_contractee",
    )

    page, paginator = get_pagination(request, contractees)

    return render(
        request,
        "contracts/view_contractees.html",
        {
            **get_base_context(request),
            "title": "Naše smluvní strany",
            "description": "Naše smluvní strany.",
            "page": page,
            "paginator": paginator,
        },
    )


def view_signees(request):
    contractees = get_objects_for_user(
        request.user,
        "contracts.view_signee",
    )

    page, paginator = get_pagination(request, contractees)

    return render(
        request,
        "contracts/view_signees.html",
        {
            **get_base_context(request),
            "title": "Jiné smluvní strany",
            "description": "Jiné smluvní strany.",
            "page": page,
            "paginator": paginator,
        },
    )


# END Submodel listing views


# Basic contract view API
def view_contract_json(request, id: int):
    filter = models.Q(status=Contract.StatusTypes.APPROVED)

    if not request.user.has_perm("contracts.view_confidential"):
        filter = filter & (
            models.Q(is_public=True)
            | (
                models.Q(created_by=request.user)
                if not request.user.is_anonymous
                else models.Value(True)
            )
        )

    contract = get_object_or_404(
        (get_objects_for_user(request.user, "contracts.view_contract").filter(filter)),
        id=id,
    )

    return JsonResponse({
        "id": contract.id,
        "created_by": {
            "id": contract.created_by_id,
            "username": contract.created_by.username,
        },
        "created_on": contract.created_on,
        "updated_on": contract.updated_on,
        "status": contract.status,
        "name": contract.name,
        "id_number": contract.id_number,
        "types": [
            {
                "id": type.id,
                "name": type.name
            }
            for type in contract.types.all()
        ],
        "summary": contract.summary,
        "valid_start_date": contract.valid_start_date,
        "valid_end_date": contract.valid_end_date,
        "is_valid": contract.is_valid,
        "is_public": contract.is_public,
        "publishing_rejection_comment": contract.publishing_rejection_comment,
        "paper_form_state": contract.paper_form_state,
        "paper_form_person_responsible": contract.paper_form_person_responsible,
        "tender_url": contract.tender_url,
        "issues": [
            {
                "id": issue.id,
                "name": issue.name
            }
            for issue in contract.issues.all()
        ]

    })


# ARES CORS proxy
def get_ares_info(request, ico: int):
    if not request.user.is_staff:
        raise HTTPExceptions.FORBIDDEN

    ares_info = requests.get(
        f"https://ares.gov.cz/ekonomicke-subjekty-v-be/rest/ekonomicke-subjekty/{ico}"
    )

    return HttpResponse(
        content=ares_info.content,
        status=ares_info.status_code,
        content_type=ares_info.headers.get("Content-Type"),
    )


def handle_404(request, exception):
    path = os.path.normpath(request.get_full_path())

    archive_url = f"https://smlouvy-archiv.pirati.cz{path}"
    archive_response = requests.get(archive_url)

    # Quick dirty check for whether the page found is an actual contract
    was_found = archive_response.ok and "Datum podpisu" in str(archive_response.content)

    return render(
        request,
        "contracts/404.html",
        {
            **get_base_context(request),
            "title": "Stránka nenalezena",
            "description": "",
            "archive_page_exists": was_found,
            "archive_url": archive_url,
        },
    )