from typing import List

class ComponentRepository:
    """
    Repository class for component-specific database operations
    """
    def __init__(self, db_connection):
        self.db = db_connection

    def save_component(self, technical_name, functional_name, id_type, folder, class_name, status):
        cursor = None
        try:
            cursor = self.db.connection.cursor()
            upsert_query = """
                INSERT INTO schmetrc.component (technical_name, functional_name, type_id, folder, class_name, status)
                VALUES (%s, %s, %s, %s, %s, %s)
                ON CONFLICT (technical_name, type_id)
                DO UPDATE SET
                    functional_name = EXCLUDED.functional_name,
                    folder = EXCLUDED.folder,
                    class_name = EXCLUDED.class_name,
                    status = EXCLUDED.status
                RETURNING id;
            """
            cursor.execute(upsert_query, (technical_name, functional_name, id_type, folder, class_name, status))
            component_id = cursor.fetchone()[0]
            self.db.commit_transaction()
            return component_id

        except Exception as e:
            self.db.rollback_transaction()
            raise Exception(f"Error saving component '{technical_name}': {str(e)}")
        finally:
            if cursor:
                cursor.close()

    def save_version(self, version, component_id, creation_date, status):
        cursor = None
        try:
            cursor = self.db.connection.cursor()

            insert_query = """
                INSERT INTO schmetrc.component_version(version, component_id, creation_date, status)
                VALUES (%s, %s, %s, %s)
                ON CONFLICT (version, component_id)
                DO UPDATE SET
                    creation_date = EXCLUDED.creation_date,
                    status = EXCLUDED.status
                RETURNING id;
            """

            cursor.execute(insert_query, (version, component_id, creation_date, status))
            component_id = cursor.fetchone()[0]
            self.db.commit_transaction()

            return component_id

        except Exception as e:
            self.db.rollback_transaction()
            raise Exception(f"Error saving version '{version}': {str(e)}")
        finally:
            if cursor:
                cursor.close()

    def query_components_by_type(self, id_type: int) -> list:
        """
        Consulta componentes de un tipo específico en la base de datos

        Args:
            id_type (int): ID del tipo de componente a consultar
        """
        cursor = None
        try:
            cursor = self.db.connection.cursor()

            query = """
            SELECT id, technical_name, type_id, status, functional_name, folder, class_name 
            FROM schmetrc.component 
            WHERE type_id = %s
            ORDER BY technical_name
            """

            cursor.execute(query, (id_type,))
            results = cursor.fetchall()

            components = []
            for row in results:
                component = {
                    'id': row[0],
                    'technical_name': row[1],
                    'type_id': row[2],
                    'status': row[3],
                    'functional_name': row[4],
                    'folder': row[5],
                    'class_name': row[6]
                }
                components.append(component)

            return components

        except Exception as e:
            raise Exception(f"Error querying components by type {id_type}: {str(e)}")
        finally:
            if cursor:
                cursor.close()

    def query_back_components(self, is_required_all_name=False) -> List[dict]:
        """
        Query back-end components from the database and return a list of dictionaries.
        This method:
          - Opens a DB cursor from `self.db.connection`.
          - Executes a SQL query to select `id` and `technical_name` from `component` table
            where `technical_name` contains the substring 'ms'.
          - Builds a list of components as dictionaries:
              * If `is_required_all_name` is True:
                  {'id': <id>, 'name': <technical_name>}
              * If `is_required_all_name` is False:
                  {'id': <id>, 'name': <technical_name[6:]>}
                (i.e., the component name is sliced starting at index 6.)
          - Ensures the cursor is closed in a `finally` block.

        Parameters
        ----------
        is_required_all_name : bool, optional
            When True, returns the full `technical_name`. When False, returns the name
            with the first 6 characters removed.
        """
        cursor = None
        try:
            cursor = self.db.connection.cursor()

            query = """
            SELECT id, technical_name
            FROM schmetrc.component 
            WHERE technical_name LIKE '%-ms-%'
            """

            cursor.execute(query)
            results = cursor.fetchall()

            components = []
            for row in results:
                if is_required_all_name:
                    components.append({
                        'id': row[0],
                        'name': row[1],
                    })
                else:
                    components.append({
                        'id': row[0],
                        'name': row[1][6:]
                    })

            return components

        except Exception as e:
            raise Exception(f"Error querying back components : {str(e)}")
        finally:
            if cursor:
                cursor.close()

    def query_versions_by_type(self, type_id: int) -> list:
        """
        Consulta versiones de un tipo específico (mobile o web) en la base de datos

        Args:
            type_id (int): ID del tipo (13 para mobile, 12 para web)
            
        Returns:
            list: Lista de versiones con sus IDs
        """
        cursor = None
        try:
            cursor = self.db.connection.cursor()
            query = """
            SELECT id, version, type_id, creation_date
            FROM schmetrc.general_version
            WHERE type_id = %s
            ORDER BY version DESC
            """
            cursor.execute(query, (type_id,))
            results = cursor.fetchall()
            
            # Get column names from cursor description
            column_names = [desc[0] for desc in cursor.description]

            versions = []
            for row in results:
                # Crear versión dinámicamente usando los nombres de columnas
                version = dict(zip(column_names, row))
                versions.append(version)

            return versions

        except Exception as e:
            raise RuntimeError(f"Error querying versions by type {type_id}: {str(e)}")
        finally:
            if cursor:
                cursor.close()

    def save_adoption(self, version_id: int, component_id: int, application_code: str, count: int, date_id: int = None):
        """
        Inserta un registro de adopción en la tabla schmetrc.general_adoption
        
        Args:
            version_id (int): ID de la versión del sistema de diseño
            component_id (int): ID del componente
            application_code (str): Código de aplicación (ej: NU0296001)
            count (int): Cantidad de usos del componente
            date_id (int, optional): ID de la fecha en measurement_systems_history. Por defecto None
        """
        cursor = None
        try:
            cursor = self.db.connection.cursor()
            insert_query = """
                INSERT INTO schmetrc.general_adoption(version_id, component_id, application_code, count, date_id) 
                VALUES (%s, %s, %s, %s, %s)
                RETURNING id;
            """
            
            cursor.execute(insert_query, (version_id, component_id, application_code, count, date_id))
            adoption_id = cursor.fetchone()[0]
            self.db.commit_transaction()
            return adoption_id
        except Exception as e:
            self.db.rollback_transaction()
            raise RuntimeError(f"Error saving adoption for component {component_id}: {str(e)}")
        finally:
            if cursor:
                cursor.close()

    def save_component_roi(self, data: List[dict]):
        """
        Insert component ROI records into the `component_roi` table and return all ids inserted.
        This method:
          - Acquires a DB cursor from `self.db.connection`.
          - Executes an `INSERT ... RETURNING id` statement for each row in `data`.
          - Calls `cursor.fetchone()` once after the loop to obtain the `id` returned by the
            last executed INSERT.
          - Commits the transaction via `self.db.commit_transaction()` on success.
          - Rolls back the transaction via `self.db.rollback_transaction()` and raises a
            `RuntimeError` with a descriptive message on failure.
          - Ensures the cursor is closed in a `finally` block.
        """
        cursor = None
        try:
            cursor = self.db.connection.cursor()
            insert_query = """
                INSERT INTO schmetrc.component_roi(component_id, application_code, creation_time, deployment_time, 
                saving_hours, cost_hours, date_id) 
                VALUES (%s, %s, %s, %s, %s, %s, %s)
                RETURNING id;
            """
            ids = []
            for row in data:
                cursor.execute(insert_query, (row["component_id"], row["application_code"], row["development_time"],
                                              row["deployment_time"], row["hours_saving"], row["hours_cost"],
                                              self.get_or_create_current_month_date()))
                ids.append(cursor.fetchone()[0])
            self.db.commit_transaction()
            return ids
        except Exception as e:
            self.db.rollback_transaction()
            raise RuntimeError(f"Error saving components roi: {str(e)}")
        finally:
            if cursor:
                cursor.close()

    def get_or_create_current_month_date(self) -> int:
        cursor = None
        try:
            self.db.ensure_connection()
            cursor = self.db.connection.cursor()

            query_select = """
            SELECT id
            FROM schmetrc.measurement_systems_history
            WHERE EXTRACT(YEAR FROM date) = EXTRACT(YEAR FROM CURRENT_DATE)
              AND EXTRACT(MONTH FROM date) = EXTRACT(MONTH FROM CURRENT_DATE)
            LIMIT 1
            """
            cursor.execute(query_select)
            result = cursor.fetchone()

            if result:
                date_id = result[0]
                return date_id
            else:
                query_insert = """
                INSERT INTO schmetrc.measurement_systems_history (date)
                VALUES (CURRENT_DATE)
                RETURNING id
                """
                cursor.execute(query_insert)
                date_id = cursor.fetchone()[0]
                self.db.commit_transaction()
                return date_id

        except Exception as e:
            self.db.rollback_transaction()
            raise RuntimeError(f"Error getting or creating current month date: {str(e)}")
        finally:
            if cursor:
                cursor.close()

    def delete_adoption_by_date_id(self, date_id: int, type_id: int = None) -> int:
        """
        Elimina todos los registros de adopción asociados a un date_id específico.
        Opcionalmente filtra por tipo de componente (mobile=13, web=12).
        Útil para limpiar datos del mes actual antes de una nueva inserción.
        
        Args:
            date_id (int): ID de la fecha cuyos registros se eliminarán (date_id)
            type_id (int, optional): ID del tipo de componente para filtrar. Si es None, elimina todos los tipos.
            
        Returns:
            int: Cantidad de registros eliminados
        """
        cursor = None
        try:
            cursor = self.db.connection.cursor()
            
            # Construir query con o sin filtro de tipo
            if type_id is not None:
                # Contar registros a eliminar (con filtro de tipo)
                count_query = """
                SELECT COUNT(*)
                FROM schmetrc.general_adoption ga
                INNER JOIN schmetrc.component c ON ga.component_id = c.id
                WHERE ga.date_id = %s AND c.type_id = %s
                """
                cursor.execute(count_query, (date_id, type_id))
                count = cursor.fetchone()[0]
                
                if count > 0:
                    # Eliminar los registros (con filtro de tipo)
                    delete_query = """
                    DELETE FROM schmetrc.general_adoption
                    WHERE id IN (
                        SELECT ga.id
                        FROM schmetrc.general_adoption ga
                        INNER JOIN schmetrc.component c ON ga.component_id = c.id
                        WHERE ga.date_id = %s AND c.type_id = %s
                    )
                    """
                    cursor.execute(delete_query, (date_id, type_id))
                    self.db.commit_transaction()
                    type_name = "mobile" if type_id == 13 else "web" if type_id == 12 else f"tipo {type_id}"
                    print(f"   🗑️ Eliminados {count} registros de adopción {type_name} del mes actual")
                else:
                    print("   ℹ️ No hay registros previos de adopción para eliminar")
            return count
            
        except Exception as e:
            self.db.rollback_transaction()
            raise RuntimeError(f"Error deleting adoption records for date_id {date_id}: {str(e)}")
        finally:
            if cursor:
                cursor.close()