from qgis.core import (QgsField, QgsProject, QgsVectorLayer, QgsWkbTypes,
                       QgsFieldConstraints, QgsEditorWidgetSetup,
                       QgsTextFormat, QgsTextBufferSettings,
                       QgsVectorLayerSimpleLabeling, QgsPalLayerSettings,
                       QgsTextBackgroundSettings, QgsDistanceArea, QgsDefaultValue)
from PyQt5.QtCore import QVariant
from PyQt5.QtGui import QFont, QColor

class CamadaPoligonosManager:
    """
    Gerencia a criação e a manutenção de camadas temporárias de polígonos,
    atualizando perímetro/área automaticamente e configurando rótulos.
    """

    def __init__(self, iface):
        """
        :param iface: instância da interface QGIS (iface)
        """
        self.iface = iface

    def criar_camada_poligonos(self, nome_camada: str | None = None):
        """
        Cria e adiciona uma nova camada temporária de polígonos ao projeto,
        já pronta para edição.

        :param nome_camada: Nome desejado; se None, será gerado automaticamente.
        :return: QgsVectorLayer criada
        """
        crs_projeto = QgsProject.instance().crs().authid()
        nome_camada = nome_camada or self.gera_nome_camada("Polígono Temp")

        # 1) Cria a camada em memória
        vl = QgsVectorLayer(f"Polygon?crs={crs_projeto}", nome_camada, "memory")

        # 2) Configura campos, rótulos e sinais
        self.configura_campos(vl)
        self.configura_etiquetas(vl)
        self.conectar_sinais(vl)

        # 3) Adiciona ao projeto e inicia edição
        proj = QgsProject.instance()
        proj.addMapLayer(vl, False)          # evita inserir automático
        proj.layerTreeRoot().insertLayer(0, vl)

        # Entra em modo de edição
        vl.startEditing()

        # 4) Ativa a camada no painel e a ferramenta de adicionar ponto
        self.iface.setActiveLayer(vl)                  # torna esta a camada ativa
        action = self.iface.actionAddFeature()         # pega ação "Adicionar feição"
        if action:                                     # se a ação existir
            action.trigger()   

        return vl

    def gera_nome_camada(self, nome_base: str) -> str:
        """
        Esta função gera um nome único para uma camada com base em um nome base fornecido.
        Ela faz isso adicionando um contador ao nome base e incrementando-o até que um nome único seja encontrado.
        """
        contador = 1
        nome_camada = f"{nome_base} {contador}"  # Começa a contagem a partir de 1
        while QgsProject.instance().mapLayersByName(nome_camada): # Verifica se o nome já existe
            contador += 1
            nome_camada = f"{nome_base} {contador}"  # Atualiza o nome da camada com o novo contador
        return nome_camada # Retorna o nome único

    def conectar_sinais(self, camada: QgsVectorLayer):
        """
        Esta função conecta os sinais 'featureAdded' e 'geometryChanged' à função 'atualizar_valores_poligono'.
        Isso garante que os valores do polígono sejam atualizados sempre que um recurso for adicionado ou sua geometria alterada.
        """
        camada.featureAdded.connect(lambda fid: self.atualizar_valores_poligono(camada, fid)) # Conecta o sinal 'featureAdded'
        camada.geometryChanged.connect(lambda fid, geom: self.atualizar_valores_poligono(camada, fid)) # Conecta o sinal 'geometryChanged'

    def atualizar_valores_poligono(self, camada: QgsVectorLayer, fid: int):
        """
        Esta função atualiza os valores de 'Perimetro' e 'Area' de um polígono quando ele é adicionado ou sua geometria é alterada.
        Considera cálculos específicos caso o sistema de referência seja geográfico.
        """
        index_perimetro = camada.fields().indexOf("Perimetro")
        index_area = camada.fields().indexOf("Area")

        feature = camada.getFeature(fid)
        if feature.isValid() and feature.geometry() and not feature.geometry().isEmpty():
            geom = feature.geometry()

            d = QgsDistanceArea()
            d.setSourceCrs(camada.crs(), QgsProject.instance().transformContext())
            d.setEllipsoid(QgsProject.instance().crs().ellipsoidAcronym())

            if camada.crs().isGeographic():
                perimetro = round(d.measurePerimeter(geom), 3)
                area = round(d.measureArea(geom), 3)
            else:
                perimetro = round(geom.length(), 3)
                area = round(geom.area(), 3)

            camada.changeAttributeValue(fid, index_perimetro, perimetro)
            camada.changeAttributeValue(fid, index_area, area)

    def configura_campos(self, camada):
        """
        Esta função configura os campos de uma camada.
        Ela adiciona campos para 'ID', 'Perimetro' e 'Area', e configura suas restrições e widgets.
        """
        id_field = QgsField("ID", QVariant.Int) # Cria um campo 'ID'
        perimetro_field = QgsField("Perimetro", QVariant.Double, "double", 20, 3)  # 20 dígitos, 3 decimais
        area_field = QgsField("Area", QVariant.Double, "double", 20, 3) # Cria um campo 'Area'

        constraints = QgsFieldConstraints() # Cria um novo objeto de restrições
        constraints.setConstraint(QgsFieldConstraints.ConstraintUnique) # Define a restrição 'Unique'
        constraints.setConstraint(QgsFieldConstraints.ConstraintNotNull) # Define a restrição 'NotNull'
        id_field.setConstraints(constraints) # Aplica as restrições ao campo 'ID'

        # Adiciona campos à camada
        camada.dataProvider().addAttributes([id_field, perimetro_field, area_field])
        camada.updateFields() # Atualiza os campos da camada

        idx_id = camada.fields().indexOf("ID")
        expr   = 'coalesce(maximum("ID"),0) + 1'
        camada.setDefaultValueDefinition(idx_id, QgsDefaultValue(expr))

        # Oculta os campos Perímetro e Área inicialmente
        widget_setup_oculto = QgsEditorWidgetSetup("Hidden", {}) # Cria um novo setup de widget oculto
        # Configura os widgets dos campos 'Perimetro' e 'Area' para serem ocultos
        camada.setEditorWidgetSetup(camada.fields().indexOf("Perimetro"), widget_setup_oculto)
        camada.setEditorWidgetSetup(camada.fields().indexOf("Area"), widget_setup_oculto)



    def configura_etiquetas(self, camada: QgsVectorLayer):
        """
        Esta função configura as etiquetas de uma camada.
        Ela habilita as etiquetas, define o campo de etiqueta para 'ID' e configura o formato das etiquetas.
        """
        settings_etiqueta = QgsPalLayerSettings() # Cria um novo objeto de configurações de etiqueta
        settings_etiqueta.fieldName = "ID" # Define o campo de etiqueta para 'ID'
        settings_etiqueta.enabled = True # Habilita as etiquetas

        text_format = QgsTextFormat() # Cria um novo objeto de formato de texto
        text_format.setColor(QColor(0, 0, 255)) # Define a cor do texto para azul
        fonte_etiqueta = QFont("Arial", 14, QFont.Bold, True)  # Cria uma nova fonte para as etiquetas, negrito e itálico
        text_format.setFont(fonte_etiqueta) # Define a fonte das etiquetas

        background_settings = QgsTextBackgroundSettings() # Cria um novo objeto de configurações de fundo
        background_settings.setEnabled(True) # Habilita o fundo
        background_settings.setFillColor(QColor(255, 255, 255)) # Define a cor de preenchimento do fundo para branco
        text_format.setBackground(background_settings) 

        settings_etiqueta.setFormat(text_format) # Aplica o formato de texto às configurações de etiqueta
        camada.setLabelsEnabled(True) # Habilita as etiquetas na camada
        camada.setLabeling(QgsVectorLayerSimpleLabeling(settings_etiqueta)) # Aplica as configurações de etiqueta à camada
