# -*- coding: utf-8 -*-
from django.shortcuts import render, redirect
from django.http import HttpRequest, HttpResponse
from django.core.mail import send_mail
from django.db.models import ExpressionWrapper, FloatField, F, Count
from django.db.models.functions import Abs
from smtplib import SMTPException
from oknardia.models import Seria_Info, Building_Info, Apartment_Type
from web.add_func import get_yandex_geocode_by_address, get_geo_distance
import json
import datetime
import time
import pytils

# from django.core.context_processors import csrf


def main_init(request: HttpRequest) -> HttpResponse:
    """ Главная страница (статичная, только с проверками куков)

    :param request: входящий http-запрос
    :return response: исходящий http-ответ
    """
    to_template: dict[str, object] = {}  # словарь, для передачи шаблону
    num_viz = 0  # как будто первый визит
    # проверяем куки числа визита
    if "NumVisit" in request.COOKIES:
        # стоят куки, и это не первый визит
        num_viz = request.COOKIES["NumVisit"]  # читаем число визитов
        num_viz = int(num_viz) + 1  # увеличиваем порядковый номер визитов
    # ПРОВЕРЯЧЕМ КУКИ ПРОСМОТРЕ ЦЕНОВЫХ ПРЕДЛОЖЕНИЙ
    if "LastVisit" in request.COOKIES:
        # стоят куки
        last_visit = json.loads(request.COOKIES["LastVisit"])
        last_visit2 = []
        for i in last_visit:
            last_visit2.append({
                "Time": datetime.datetime.fromtimestamp(i["Time"]),
                "LastURL": i["LastURL"],
                "LastAddress": i["LastAddress"],
                "LastApart": i["LastApart"]
            })
        to_template.update({'LAST_VISIT': last_visit2[:3]})
    else:
        to_template.update({'LAST_VISIT': None})
    to_template.update({'META_DOCUMENT_STATE': u"Static"})      # Эта страничка статичная (в шаблон)
    to_template.update({'NV': num_viz})
    # to_template.update(csrf(request))                           # токен, для метода POST и GET
    response = render(request, "index.html", to_template)
    response.set_cookie("NumVisit", num_viz, max_age=604800)    # ставим или перезаписываем куки (неделя)
    return response


def tariff(request: HttpRequest) -> HttpResponse:
    """ Показывает страничку с тарифами (статика + отправка почты)

    :param request: входящий http-запрос
    :return response: исходящий http-ответ
    """
    to_template: dict[str, object] = {}      # для передачи в шаблон
    if request.method == 'POST':
        # print request.POST
        if 'tariff' in request.POST and 'email_' in request.POST \
                and 'fio_' in request.POST \
                and 'tel_' in request.POST \
                and 'accompanying_message' in request.POST:
            message = "---"
            if request.POST['tariff'] == "1":
                message = "{α} альфа — разместить свои цены на «Окнардии» (бесплатно)"
            elif request.POST['tariff'] == "2":
                message = "{β} бета — разместить свои цены на «Окнардии»"
            elif request.POST['tariff'] == "3":
                message = "{δ} дельта — разместить баннеры"
            elif request.POST['tariff'] == "4":
                message = "{ω} омега — виджет на свой сайт и размещение цены на «Окнардии»"
            elif request.POST['tariff'] == "5":
                message = "Другая форма сотрудничества и/или предложение"
            message = f"ЗАПРОС НА СОТРУДНИЧЕСТВО\n\nВы (или кто-то вместо вас) отправил запрос на сотрудничество " \
                      f"с оконным\n агрегатором «Окнардия». Указан:\n email — {request.POST['email_']}\n" \
                      f" телефон — {request.POST['tel_']}\n имя — {request.POST['fio_']}\n\nЗапрос поступил на " \
                      f"сотрудничество по тарифу:\n{message}\n\nВ качестве сопроводительного сообщения:\n" \
                      f"-----------------------------------------------\n{request.POST['accompanying_message']}\n" \
                      f"-----------------------------------------------\n\nМы обязательно свяжемся с вами в" \
                      f" ближайшее время.\n\n\n~~~~~~~~~~~~\nС уважением,\nАдминистрация оконного агрегатора" \
                      f" «Окнардии»\n\nhttps://oknardia.ru  (info@oknardia.ru)\n"
            try:
                # Собираем почтовое сообщение для себе
                send_mail('OKNARDIA_TO__ADMIN: ЗАПРОС НА СОТРУДНИЧЕСТВО', message,
                          'info@oknardia.ru', ['erjemin@gmail.com', 't@oknardia.ru'], fail_silently=False)
                # Собираем почтовое сообщение для клиента
                send_mail('ОКНАРДИЯ: запрос на сотрудничество', message,
                          'info@oknardia.ru',  [request.POST['email_']], fail_silently=False)
                to_template.update({'SENDER': "Ok!"})
            except SMTPException:
                # Что-то пошло не так и почта не отправилась. Надо подумать что в этим делать
                to_template.update({'SENDER': "Error!"})
                pass
    return render(request, "tariff.html", to_template)


def contact(request: HttpRequest) -> HttpResponse:
    """ Показывает страничку с контактной информацией

    :param request: входящий http-запрос
    :return response: исходящий http-ответ
    """
    return render(request, "contact.html", {})


def _fmt(value: object, fmt: str = ".1f", threshold: float = 0, default: str = "Нет данных") -> str:
    """Вспомогательная функция: форматирует числовое поле здания или возвращает заглушку.

    :param value:     значение поля модели (числовое)
    :param fmt:       строка формата для f-string, например '.1f' или '.0f'
    :param threshold: значения < threshold считаются «нет данных» (обычно 0 или -1)
    :param default:   строка-заглушка при отсутствии данных
    """
    try:
        if float(value) < threshold:
            return default
        return f"{value:{fmt}}"
    except (TypeError, ValueError):
        return default


def get_address(request: HttpRequest) -> HttpResponse:
    """ Вызывается после ввода пользователем адреса. Получает строку с адресом методом POST

    ВНИМАНИЕ ТЕХНИЧЕСКИЙ ДОЛГ: Заменить GET на POST

    :param request: request
    :return: response
    ░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░"""
    time_start = time.perf_counter()
    if request.method != 'POST':
        return redirect("/")
    if 'address' not in request.POST:
        return redirect("/")
    addr = request.POST['address']
    to_template: dict[str, object] = {}
    try:
        q = Building_Info.objects.get(sAddress=addr)
        # Если QuerySet не содержит GeoCode (такое бывает, что в Яндекс-Картах не было каких-то данных),
        # то пробуем получить GeoCode повторно (вдруг у Яндекс-Карт расширилась база адресов)
        if int(q.fGeoCode_Longitude) != 0 and int(q.fGeoCode_Latitude != 0):
            # print("координаты не ноль")
            to_template.update({'LATITUDE':  str(q.fGeoCode_Latitude).replace(",", "."),
                                'LONGITUDE': str(q.fGeoCode_Longitude).replace(",", ".")})
            geocode = [q.fGeoCode_Latitude, q.fGeoCode_Longitude]
        else:
            # print("координаты ноль")
            geocode = get_yandex_geocode_by_address(addr)
            # print("получен геокод", geocode)
            to_template.update({'LATITUDE':  str(geocode[0]).replace(",", "."),
                                'LONGITUDE': str(geocode[1]).replace(",", ".")})
    except:
        # print("тут")
        geocode = get_yandex_geocode_by_address(addr)
        # print(geocode)
        to_template.update({'LATITUDE':  str(geocode[0]).replace(",", ".")})
        to_template.update({'LONGITUDE': str(geocode[1]).replace(",", ".")})
        # Ищем ближайшее здание по манхэттенскому расстоянию (lat/lon в градусах, ~0.01 ≈ 1 км)
        q = (Building_Info.objects
             .annotate(
                 R2=ExpressionWrapper(
                     Abs(float(geocode[0]) - F('fGeoCode_Latitude'))
                     + Abs(float(geocode[1]) - F('fGeoCode_Longitude')),
                     output_field=FloatField()
                 )
             )
             .order_by('R2')
             .first())
        if q is None or q.R2 > 0.67:  # Если расстояние > ~670 метров или ничего нет — не показываем
            to_template.update({'ticks': float(time.perf_counter()-time_start)})
            to_template.update({'addr': addr})
            return render(request, "popup/popup_incorrect_address.html", to_template)
    addr = q.sAddress
    # print("addr", addr)
    to_template.update({
        'ADDRESS_ID':          q.id,
        'SERIA':               q.sSerias_Project,
        'TOTAL_AREA':          _fmt(q.fTotal_Area),
        'CADASTRE_NUM':        q.sCadastre_Num_Area,
        'LAND':                _fmt(q.fLand_Area),
        'INVENTORY_NUM':       q.sInventory_Num,
        'NUM_APARTMENTS':      q.iNum_Apartments     if q.iNum_Apartments >= 0    else "Нет данных",
        'TYPE_BUILDING':       q.sType,
        'STOREYS':             q.iStoreys            if q.iNum_Apartments >= 0    else "Нет данных",
        'COMMON_AREA':         _fmt(q.fCommon_Area),
        'ENERGY_EFFICIENCY':   q.sEnergy_Efficiency,
        'NUM_ENTERANCES':      q.iEntrances_Porchs   if q.iEntrances_Porchs >= 0  else "Нет",
        'UNINHABITED_AREA':    _fmt(q.fUninhabited_Area),
        'MANAGEMENT_CO':       q.sManagement_Co      if q.sManagement_Co != "N/A" else "Нет данных",
        'NUM_ELEVATORS':       q.iElevators          if q.iElevators >= 0         else "Нет данных",
        'RESIDENTIAL_AREA':    _fmt(q.fResidential_Area),
        'NUM_RESIDENTS':       q.iNum_Residents      if q.iNum_Residents >= 0     else "Нет данных",
        'PRIVATE_AREA':        _fmt(q.fPrivate_Area),
        'NUM_ACCOUNTS':        q.iNum_Accounts       if q.iNum_Accounts >= 0      else "Нет данных",
        'COMMISSIONING_YEAR':  q.iCommissioning_year if q.iCommissioning_year != "N/A" else "Нет данных",
        'GOVERNMENT_AREA':     _fmt(q.fGovernment_Area),
        'CONDITION_HOUSE':     _fmt(q.fCondition_House,       fmt=".0f", default="Нет данных") + "%" if q.fCondition_House >= 0      else "Нет данных",
        'CONDITION_FOUNDATION': _fmt(q.fCondition_Foundation, fmt=".0f", default="Нет данных") + "%" if q.fCondition_Foundation >= 0 else "Нет данных",
        'CONDITION_WALL':      _fmt(q.fCondition_Walls,       fmt=".0f", default="Нет данных") + "%" if q.fCondition_Walls >= 0      else "Нет данных",
        'CONDITION_OVERLAP':   _fmt(q.fCondition_Overlap,     fmt=".0f", default="Нет данных") + "%" if q.fCondition_Overlap >= 0    else "Нет данных",
        'MUNICIPAL_AREA':      _fmt(q.fMunicipal_Area),
        'URL2REFOEMAGKH':      q.sURL,
    })
    # Пробуем получить базовую серию дома. Для этого рекурсивно раскручиваем записи в таблице Seria_Info
    idd = q.kSeria_Link_id
    all_apartment_in_seria = False
    q1 = None  # страховка: если у здания нет серии, q1 остаётся None
    while idd is not None:
        # рекурсивно движемся по дерву потомок→предок серий домов.
        q1 = Seria_Info.objects.select_related('kRoot').get(id=idd)
        # получаем список типовых квартир для серии дома с id == idd
        all_apartment_in_seria = Apartment_Type.objects.filter(kSeria_id=idd).order_by("iSort")
        # проверяем есть-ли что-то в списке типовых квартир.
        if bool(all_apartment_in_seria):
            # список типовых квартир не нулевой
            to_template.update({'LIST_APART': all_apartment_in_seria})
            break
        idd = q1.kParent_id
    # проверяем, был ли получен список квартир
    if not bool(all_apartment_in_seria):
        # Если списка квартир нет, нужно получить список ближайших адресов, для которых есть цены.
        # Ищем здания с ненулевыми координатами и у которых через серию есть типовые квартиры.
        q = list(
            Building_Info.objects
            .exclude(fGeoCode_Longitude=0.0)
            .exclude(fGeoCode_Latitude=0.0)
            .filter(kSeria_Link__kRoot__apartment_type__isnull=False)
            .select_related('kSeria_Link', 'kSeria_Link__kRoot')
            .annotate(
                R2=ExpressionWrapper(
                    Abs(float(geocode[0]) - F('fGeoCode_Latitude'))
                    + Abs(float(geocode[1]) - F('fGeoCode_Longitude')),
                    output_field=FloatField()
                ),
                NumApart=Count('kSeria_Link__kRoot__apartment_type', distinct=True),
                sName=F('kSeria_Link__sName'),
                kRoot_id=F('kSeria_Link__kRoot_id'),
            )
            .order_by('R2')[:5]
        )
        for i in q:
            # Пересчитываем на реальное геодезическое расстояние (км)
            i.R2 = get_geo_distance(i.fGeoCode_Longitude, i.fGeoCode_Latitude, geocode[0], geocode[1])
            # print i.id, i.sAddress, i.sName, i.R2
        # сортируем список по R2 (дистанция от текущего адреса, до домов по которым данные известны)
        q = sorted(q, key=lambda item: item.R2)  # NOTE: sorted() возвращает новый список
        to_template.update({'NEAR_KNOWN_ADDRESS': q})
        # print q
    # Определяем корневую серию для формирования канонического URL
    # Если у серии есть kRoot — берём его, иначе сама q1 является корневой
    seria_root = (q1.kRoot if (q1 and q1.kRoot_id) else q1)
    to_template.update({
        'SERIA_BASE': q1.sName if q1 else "",
        'BASE_SERIA_ID': seria_root.id if seria_root else "",
        'BASE_SERIA_LAT': pytils.translit.slugify((seria_root.sName or "").strip()).lower() if seria_root else "",
        'addr': addr,
        'addr_T': pytils.translit.slugify(addr),
        'ticks': float(time.perf_counter() - time_start),
    })
    return render(request, "popup/popup_show_apartment_variants.html", to_template)
