import os
import logging
from sqlite3 import Row
from typing import Any, TYPE_CHECKING
from urllib.parse import urlparse

from flask import current_app, url_for

from .exceptions import ErrorException
from .rebrickable_image import RebrickableImage
from .record import BrickRecord
if TYPE_CHECKING:
    from .minifigure import BrickMinifigure
    from .set import BrickSet
    from .socket import BrickSocket

logger = logging.getLogger(__name__)


# A part from Rebrickable
class RebrickablePart(BrickRecord):
    socket: 'BrickSocket'
    brickset: 'BrickSet | None'
    minifigure: 'BrickMinifigure | None'

    # Queries
    select_query: str = 'rebrickable/part/select'
    insert_query: str = 'rebrickable/part/insert'

    def __init__(
        self,
        /,
        *,
        brickset: 'BrickSet | None' = None,
        minifigure: 'BrickMinifigure | None' = None,
        record: Row | dict[str, Any] | None = None
    ):
        super().__init__()

        # Save the brickset
        self.brickset = brickset

        # Save the minifigure
        self.minifigure = minifigure

        # Ingest the record if it has one
        if record is not None:
            self.ingest(record)

    # Insert the part from Rebrickable
    def insert_rebrickable(self, /) -> None:
        if self.brickset is None:
            raise ErrorException('Importing a part from Rebrickable outside of a set is not supported')  # noqa: E501

        # Insert the Rebrickable part to the database
        self.insert(
            commit=False,
            no_defer=True,
            override_query=RebrickablePart.insert_query
        )

        if not current_app.config['USE_REMOTE_IMAGES']:
            RebrickableImage(
                self.brickset,
                minifigure=self.minifigure,
                part=self,
            ).download()

    # Return a dict with common SQL parameters for a part
    def sql_parameters(self, /) -> dict[str, Any]:
        parameters = super().sql_parameters()

        # Set id
        if self.brickset is not None:
            parameters['id'] = self.brickset.fields.id

        # Use the minifigure number if present,
        if self.minifigure is not None:
            parameters['figure'] = self.minifigure.fields.figure
        else:
            parameters['figure'] = None

        return parameters

    # Self url
    def url(self, /) -> str:
        return url_for(
            'part.details',
            part=self.fields.part,
            color=self.fields.color,
        )

    # Compute the url for the bricklink page
    def url_for_bricklink(self, /) -> str:
        if current_app.config['BRICKLINK_LINKS']:
            try:
                return current_app.config['BRICKLINK_LINK_PART_PATTERN'].format(  # noqa: E501
                    part=self.fields.part,
                )
            except Exception:
                pass

        return ''

    # Compute the url for the part image
    def url_for_image(self, /) -> str:
        if not current_app.config['USE_REMOTE_IMAGES']:
            if self.fields.image is None:
                file = RebrickableImage.nil_name()
            else:
                file = self.fields.image_id

            return RebrickableImage.static_url(file, 'PARTS_FOLDER')
        else:
            if self.fields.image is None:
                return current_app.config['REBRICKABLE_IMAGE_NIL']
            else:
                return self.fields.image

    # Compute the url for the original of the printed part
    def url_for_print(self, /) -> str:
        return url_for(
            'part.details',
            part=self.fields.print,
            color=self.fields.color,
        )

    # Compute the url for the rebrickable page
    def url_for_rebrickable(self, /) -> str:
        if current_app.config['REBRICKABLE_LINKS']:
            try:
                if self.fields.url is not None:
                    # The URL does not contain color info...
                    return '{url}{color}'.format(
                        url=self.fields.url,
                        color=self.fields.color
                    )
                else:
                    return current_app.config['REBRICKABLE_LINK_PART_PATTERN'].format(  # noqa: E501
                        part=self.fields.part,
                        color=self.fields.color,
                    )
            except Exception:
                pass

        return ''

    # Normalize from Rebrickable
    @staticmethod
    def from_rebrickable(
        data: dict[str, Any],
        /,
        *,
        brickset: 'BrickSet | None' = None,
        minifigure: 'BrickMinifigure | None' = None,
        **_,
    ) -> dict[str, Any]:
        record = {
            'id': None,
            'figure': None,
            'part': data['part']['part_num'],
            'color': data['color']['id'],
            'spare': data['is_spare'],
            'quantity': data['quantity'],
            'rebrickable_inventory': data['id'],
            'element': data['element_id'],
            'color_id': data['color']['id'],
            'color_name': data['color']['name'],
            'color_rgb': data['color']['rgb'],
            'color_transparent': data['color']['is_trans'],
            'name': data['part']['name'],
            'category': data['part']['part_cat_id'],
            'image': data['part']['part_img_url'],
            'image_id': None,
            'url': data['part']['part_url'],
            'print': data['part']['print_of']
        }

        if brickset is not None:
            record['id'] = brickset.fields.id

        if minifigure is not None:
            record['figure'] = minifigure.fields.figure

        # Extract the file name
        if record['image'] is not None:
            image_id, _ = os.path.splitext(
                os.path.basename(
                    urlparse(record['image']).path
                )
            )

            if image_id is not None or image_id != '':
                record['image_id'] = image_id

        return record