from threading import Lock

from customer_services.model import UpdateCustomerException
from typing import Any

from msgbus import MBEasyContext
from mwhelper import BaseRepository
from persistence import Connection

from customer_services.model import InsertCustomerException, Address, Customer
from customer_services.model.repository import CustomerRepository as CustomerServicesRepository


class CustomerRepository(CustomerServicesRepository, BaseRepository):

    def __init__(self, mb_context, persistence_name, database_lock):
        # type: (MBEasyContext, str, Lock) -> None

        super(CustomerRepository, self).__init__(mb_context)

        self.persistence_name = persistence_name
        self.database_lock = database_lock

    def get_customer(self, phone):
        # type: (str) -> Any[Customer, None]

        try:
            def inner_func(conn):
                # type: (Connection) -> Any[Customer, None]

                query = """
                            SELECT
                            c.Phone,
                            c.Document,
                            c.Name,
                            ca.Id,
                            ca.ZipCode,
                            ca.State,
                            ca.City,
                            ca.Street,
                            ca.NeighborHood,
                            ca.Number,
                            ca.Complement,
                            ca.ReferencePoint
                            FROM Customer c
                            JOIN CustomerAddress ca ON ca.Id = c.AddressId
                            WHERE c.Phone = '{}'
                        """
                query_format = query.format(phone)
                cursor = conn.select(query_format)
                if not cursor.rows():
                    return None

                row = cursor.get_row(0)
                address = Address(
                    row.get_entry("Id"),
                    row.get_entry("ZipCode"),
                    row.get_entry("State"),
                    row.get_entry("City"),
                    row.get_entry("Street"),
                    row.get_entry("Neighborhood"),
                    row.get_entry("Number"),
                    row.get_entry("Complement"),
                    row.get_entry("ReferencePoint")
                )
                return Customer(row.get_entry("Phone"), row.get_entry("Document"), row.get_entry("Name"), address)

            return self.execute_with_connection(inner_func, service_name=self.persistence_name)
        except Exception as _:
            raise InsertCustomerException()

    def add_customer(self, customer):
        # type: (Customer) -> None

        with self.database_lock:
            try:
                def inner_func(conn):
                    # type: (Connection) -> None

                    self._insert_address(customer.address)
                    address_id = self._get_last_inserted_address()

                    query = """
                                INSERT INTO Customer(Phone, Document, Name, AddressId)
                                VALUES ('{}', '{}', '{}', '{}')
                            """
                    query_format = query.format(customer.phone, customer.document, customer.name, address_id)
                    conn.query(query_format)

                self.execute_with_connection(inner_func, service_name=self.persistence_name)
            except Exception as _:
                raise InsertCustomerException()

    def update_customer(self, customer):
        # type: (Customer) -> None

        with self.database_lock:
            try:
                def inner_func(conn):
                    # type: (Connection) -> None

                    self._update_address(customer.address)
                    query = """
                                UPDATE Customer
                                SET
                                Document='{}',
                                Name='{}'
                                WHERE Phone='{}'
                            """
                    query_format = query.format(customer.document, customer.name, customer.phone)
                    conn.query(query_format)

                self.execute_with_connection(inner_func, service_name=self.persistence_name)
            except Exception as _:
                raise InsertCustomerException()

    def _insert_address(self, address):
        # type: (Address) -> None

        try:
            def inner_func(conn):
                # type: (Connection) -> None

                query = """
                            INSERT INTO CustomerAddress(
                                ZipCode, 
                                State, 
                                City, 
                                Street, 
                                Neighborhood, 
                                Number, 
                                Complement, 
                                ReferencePoint
                            )
                            VALUES ('{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}')
                        """
                query_format = query.format(
                    address.zip_code,
                    address.state,
                    address.city,
                    address.street,
                    address.neighborhood,
                    address.number,
                    address.complement,
                    address.reference_point
                )
                conn.query(query_format)

            self.execute_with_connection(inner_func, service_name=self.persistence_name)
        except Exception as _:
            raise UpdateCustomerException()

    def _get_last_inserted_address(self):
        # type: () -> int

        try:
            def inner_func(conn):
                # type: (Connection) -> int

                query = """
                            SELECT Id FROM CustomerAddress
                            ORDER BY Id DESC
                            LIMIT 1
                        """
                cursor = conn.select(query)
                if not cursor.rows():
                    return -1

                row = cursor.get_row(0)
                return row.get_entry("Id")

            return self.execute_with_connection(inner_func, service_name=self.persistence_name)
        except Exception as _:
            raise InsertCustomerException()

    def _update_address(self, address):
        # type: (Address) -> None

        try:
            def inner_func(conn):
                # type: (Connection) -> None

                query = """
                            UPDATE CustomerAddress
                            SET
                            ZipCode='{}',
                            State='{}',
                            City='{}',
                            Street='{}',
                            Neighborhood='{}',
                            Number='{}',
                            Complement='{}',
                            ReferencePoint='{}'
                            WHERE Id='{}'
                        """
                query_format = query.format(
                    address.zip_code,
                    address.state,
                    address.city,
                    address.street,
                    address.neighborhood,
                    address.number,
                    address.complement,
                    address.reference_point,
                    address.address_id
                )
                conn.query(query_format)

            self.execute_with_connection(inner_func, service_name=self.persistence_name)
        except Exception as _:
            raise InsertCustomerException()
