from qgis.core import (QgsField, QgsProject, QgsVectorLayer, QgsFieldConstraints, 
    QgsEditorWidgetSetup, QgsPalLayerSettings, QgsTextFormat, QgsVectorLayerSimpleLabeling, QgsUnitTypes, QgsDistanceArea, QgsSingleSymbolRenderer, QgsCategorizedSymbolRenderer, QgsEditFormConfig, QgsDefaultValue)
from PyQt5.QtGui import QFont, QColor
from PyQt5.QtCore import QVariant 
import os

class CamadaLinhasManager:
    def __init__(self, iface):
        """
        :param iface: instância do QGIS Interface (iface)
        """
        self.iface = iface

    def conectar_sinais(self, camada: QgsVectorLayer):
        """Conecta sinais relevantes para atualizações automáticas na camada."""
        camada.featureAdded.connect(lambda fid: self.atualizar_comprimento_linha(camada, fid))
        camada.geometryChanged.connect(lambda fid, geom: self.atualizar_comprimento_linha(camada, fid))

    def criar_camada_linhas(self, nome_camada=None, cor: QColor = None, crs: str = None) -> QgsVectorLayer:
        """Cria e configura uma nova camada de linhas temporária, adicionando-a ao painel de camadas do QGIS.
        
        Parâmetros:
          - iface: interface do QGIS.
          - nome_camada: nome desejado para a camada. Se None, é gerado um nome único.
          - cor: (opcional) um objeto QColor. Se fornecido, a cor é aplicada ao símbolo da camada.
          - crs: (opcional) o authid do CRS para a nova camada. Se None, utiliza o CRS do projeto.
        
        Retorna:
          A camada de linhas criada.
        """
        # 1) CRS
        if crs is None:
            crs = self.obter_crs_projeto_atual()

        # 2) Nome
        nome = self.gerar_nome_camada(nome_camada)

        # 3) Criação base
        camada = self.criar_camada_vectorial(nome, crs)

        # 4) Campos + ocultação
        self.adicionar_campos(camada)
        self.configurar_campo_oculto(camada, "Comprimento")

        # 5) Iniciar edição e conectar sinal de cálculo
        camada.startEditing()
        self.conectar_sinais(camada)

        # 6) Renderer / cor opcional
        if cor is not None:
            renderer = camada.renderer()
            if isinstance(renderer, QgsSingleSymbolRenderer):
                renderer.symbol().setColor(cor)
            elif isinstance(renderer, QgsCategorizedSymbolRenderer):
                for cat in renderer.categories():
                    cat.symbol().setColor(cor)
            camada.triggerRepaint()

        # adiciona no TOPO da lista (sem grupos)
        proj = QgsProject.instance()
        proj.addMapLayer(camada, False)          # evita inserir automático
        proj.layerTreeRoot().insertLayer(0, camada)

        # 9) Ativa a camada no painel e a ferramenta de adicionar ponto
        self.iface.setActiveLayer(camada)                  # 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 camada

    def obter_crs_projeto_atual(self) -> str:
        """Obtém o Sistema de Referência de Coordenadas (SRC) do projeto atual."""
        return QgsProject.instance().crs().authid()

    def gerar_nome_camada(self, nome: str) -> str:
        """Gera um nome único para a camada se nenhum for fornecido."""
        if not nome:
            base = "Linha Temp"
            i = 1
            while QgsProject.instance().mapLayersByName(f"{base} {i}"):
                i += 1
            return f"{base} {i}"
        return nome

    def criar_camada_vectorial(self, nome: str, crs: str) -> QgsVectorLayer:
        """Cria uma nova camada vectorial de linhas temporária."""
        uri = f"LineString?crs={crs}"
        return QgsVectorLayer(uri, nome, "memory")

    def adicionar_campos(self, camada: QgsVectorLayer):
        """Adiciona campos ID e Comprimento; ID é autoincremental."""
        id_field   = QgsField("ID", QVariant.Int)
        comp_field = QgsField("Comprimento", QVariant.Double, "double", 20, 3) # 20 dígitos, 3 decimais

        # restrições (único + não nulo)
        id_field.setConstraints(self.constraints_com_incremente())

        camada.dataProvider().addAttributes([id_field, comp_field])
        camada.updateFields()

        # Valor padrão: máximo(ID) + 1
        idx_id = camada.fields().indexOf("ID")
        expr   = 'coalesce(maximum("ID"),0) + 1'
        camada.setDefaultValueDefinition(idx_id, QgsDefaultValue(expr))

    def constraints_com_incremente(self) -> QgsFieldConstraints:
        """Define restrições para o campo de incremento."""
        c = QgsFieldConstraints()
        c.setConstraint(QgsFieldConstraints.ConstraintUnique)
        c.setConstraint(QgsFieldConstraints.ConstraintNotNull)
        return c

    def configurar_campo_oculto(self, camada: QgsVectorLayer, nome_campo: str):
        """Configura um campo da camada para ser oculto na interface."""
        idx = camada.fields().indexOf(nome_campo)
        setup = QgsEditorWidgetSetup("Hidden", {})
        camada.setEditorWidgetSetup(idx, setup)

    def atualizar_comprimento_linha(self, camada: QgsVectorLayer, fid: int):
        """Atualiza o comprimento da linha adicionada e configura etiquetas."""
        idx = camada.fields().indexOf("Comprimento")
        feat = camada.getFeature(fid)
        medida = QgsDistanceArea()
        if camada.crs().isGeographic():
            medida.setSourceCrs(camada.crs(), QgsProject.instance().transformContext())
            medida.setEllipsoid(QgsProject.instance().crs().ellipsoidAcronym())
            comp = round(medida.measureLength(feat.geometry()), 3)
        else:
            comp = round(feat.geometry().length(), 3)

        camada.changeAttributeValue(fid, idx, comp)

        # configura etiquetas na primeira vez
        if not hasattr(camada, "etiquetas_configuradas"):
            self.configurar_etiquetas(camada)
            camada.etiquetas_configuradas = True

        camada.triggerRepaint()

    def configurar_etiquetas(self, camada: QgsVectorLayer):
        """Configura as etiquetas para a camada."""
        labels = QgsPalLayerSettings()
        labels.enabled = True
        labels.fieldName = "ID"
        labels.placement = QgsPalLayerSettings.Line
        labels.setFormat(self.formato_texto_etiqueta())
        camada.setLabelsEnabled(True)
        camada.setLabeling(QgsVectorLayerSimpleLabeling(labels))
        camada.triggerRepaint()

    def formato_texto_etiqueta(self) -> QgsTextFormat:
        """Retorna o formato do texto para as etiquetas."""
        fmt = QgsTextFormat()
        font = QFont("Arial", 12, QFont.Bold, True)
        fmt.setFont(font)
        fmt.setColor(QColor("blue"))
        buf = fmt.buffer()
        buf.setEnabled(True)
        buf.setSize(3)
        buf.setColor(QColor("white"))
        return fmt

        