import textdistance
from unidecode import unidecode
from datetime import datetime
from databases import (
    BuildIbanDescription,
    BuildIbans,
    BuildDecisionBook,
    BuildLivingSpace,
    AccountRecords,
)
from typing import Optional
from pydantic import BaseModel


class InsertBudgetRecord(BaseModel):
    iban: str
    bank_date: str = datetime.now().__str__()
    receive_debit: str = "debit"
    budget_type: str = "B"
    currency_value: float = 0
    balance: float = 0
    bank_reference_code: str = ""
    currency: str = "TL"
    channel_branch: str = ""
    process_name: str = ""
    process_type: str = ""
    process_comment: str = ""

    add_xcomment: Optional[str] = None
    project_no: Optional[str] = None
    company_id: Optional[str] = None
    customer_id: Optional[str] = None

    send_person_id: Optional[int] = None
    send_company_id: Optional[int] = None
    build_id: Optional[int] = None
    build_decision_book_id: Optional[int] = None
    build_parts_id: Optional[int] = None
    build_db_item_id: Optional[int] = None
    dues_type: Optional[str] = "D"
    period_time: Optional[str] = ""
    approving_accounting_record: Optional[bool] = False
    approving_accounting_person: Optional[str] = None
    accounting_receipt_date: Optional[str] = "1900-01-01 00:00:00"
    accounting_receipt_number: Optional[int] = 0


def strip_time_date(date_str):
    return datetime.strptime(date_str, "%Y-%m-%d")


def strip_date_to_valid(date_str):
    return datetime.strptime(date_str, "%Y-%m-%d %H:%M:%S")


def find_iban_in_comment(iban: str, comment: str):
    iban_results, iban_count = BuildIbanDescription.filter_by(iban=iban)
    sm_dict_extended, sm_dict_digit = {}, {}
    # is_reference_build = any(
    #     letter in comment.lower() for letter in ["no", "daire", "nolu"]
    # )
    if iban_count:
        for iban_result in iban_results:
            candidate_parts = comment.split(" ")
            extended_candidate_parts, digit_part = [], []
            for part in candidate_parts:
                if part.lower() not in ["no", "daire", "nolu"]:
                    extended_candidate_parts.append(part)
                # if part.isdigit():
                #     digit_part.append(part)
            if extended_candidate_parts:
                if all(
                    candidate_part.lower() in comment.lower()
                    for candidate_part in extended_candidate_parts
                ):
                    similarity_ratio = textdistance.jaro_winkler(
                        unidecode(str(iban_result.search_word)), comment
                    )
                    found = False
                    name_list = (
                        unidecode(str(iban_result.search_word))
                        .replace(".", " ")
                        .split(" ")
                    )
                    for name in name_list:
                        if len(name) > 3 and name.lower() in comment.lower():
                            found = True
                            break

                    if not found:
                        similarity_ratio = 0.1
                    sm_dict_extended[f"{iban_result.id}"] = similarity_ratio

        if sm_dict_extended:
            result = sorted(
                sm_dict_extended.items(), key=lambda item: item[1], reverse=True
            )[0]
            if float(result[1]) >= 0.5:
                iban_result = BuildIbanDescription.find_one(id=int(result[0]))
                return {
                    "decision_book_project_id": iban_result.decision_book_project_id,
                    "company_id": iban_result.company_id,
                    "customer_id": iban_result.customer_id,
                    "build_parts_id": iban_result.build_parts_id,
                    "found_from": "Name",
                    "similarity": result[1],
                }
    return {
        "decision_book_project_id": None,
        "company_id": None,
        "customer_id": None,
        "build_parts_id": None,
        "found_from": None,
        "similarity": 0.0,
    }


def parse_comment_with_name(iban: str, comment: str):
    if "*" in comment:
        b_comment, a_comment = (
            unidecode(str(comment)).split("*")[0],
            unidecode(str(comment)).split("*")[1],
        )
        a_result = find_iban_in_comment(iban, a_comment)
        b_result = find_iban_in_comment(iban, b_comment)
        if a_result["similarity"] > b_result["similarity"]:
            a_result["send_person_id"] = a_result["customer_id"]
            return a_result
        else:
            b_result["send_person_id"] = None
            return b_result
    else:
        result = find_iban_in_comment(iban, comment)
        result["send_person_id"] = result.get("customer_id", None)
        return result


def wag_insert_budget_record(data):
    similarity_result = parse_comment_with_name(data["iban"], data["process_comment"])
    build_iban = BuildIbans.find_one(iban=data["iban"])

    if payload := InsertBudgetRecord(**data):
        payload_dict = payload.model_dump(exclude_unset=True, exclude_none=True)
        decision_books = BuildDecisionBook.select_only(
            BuildDecisionBook.period_start_date
            < strip_date_to_valid(payload_dict["bank_date"]),
            BuildDecisionBook.period_stop_date
            > strip_date_to_valid(payload_dict["bank_date"]),
            select_args=[BuildDecisionBook.id],
            order_by=[BuildDecisionBook.expiry_ends.desc()],
        )
        payload_dict["build_id"] = getattr(
            BuildIbans.find_one(iban=data["iban"]), "build_id", None
        )
        living_space, count = BuildLivingSpace.find_living_from_customer_id(
            similarity_result.get("customer_id", None),
            strip_date_to_valid(payload_dict["bank_date"]),
        )
        # living_space, count = BuildLivingSpace.filter(
        #     or_(
        #         BuildLivingSpace.owner_person_id
        #         == similarity_result.get("customer_id", None),
        #         BuildLivingSpace.life_person_id
        #         == similarity_result.get("customer_id", None),
        #     ),
        #     BuildLivingSpace.start_date
        #     < strip_date_to_valid(payload_dict["bank_date"]) - timedelta(days=30),
        #     BuildLivingSpace.stop_date
        #     > strip_date_to_valid(payload_dict["bank_date"]) + timedelta(days=30),
        #     BuildLivingSpace.active == True,
        #     BuildLivingSpace.deleted == False,
        # )
        payload_dict["build_decision_book_id"] = (
            decision_books[0][0].id if decision_books else None
        )
        payload_dict["company_id"] = similarity_result.get("company_id", None)
        payload_dict["customer_id"] = similarity_result.get("customer_id", None)
        payload_dict["send_person_id"] = similarity_result.get("send_person_id", None)

        payload_dict["build_parts_id"] = (
            living_space[0].build_parts_id if living_space else None
        )

        payload_dict["bank_date_y"] = strip_date_to_valid(
            payload_dict["bank_date"]
        ).year
        payload_dict["bank_date_m"] = strip_date_to_valid(
            payload_dict["bank_date"]
        ).month
        payload_dict["bank_date_d"] = strip_date_to_valid(payload_dict["bank_date"]).day
        payload_dict["bank_date_w"] = strip_date_to_valid(
            payload_dict["bank_date"]
        ).isocalendar()[2]
        payload_dict["build_id"] = build_iban.build_id if build_iban else None
        payload_dict["replication_id"] = 55
        payload_dict["receive_debit"] = (
            "R" if payload_dict["currency_value"] < 0 else "D"
        )
        data, found = AccountRecords.find_or_create(
            **payload_dict,
            found_from=similarity_result.get("found_from", None),
            similarity=similarity_result.get("similarity", 0.0),
        )
        data.payment_budget_record_close()
        return data, found
