import requests
import json
import time
import os
import mysql.connector
from datetime import datetime

# =============================================
# Constantes de caminhos
# =============================================
LOG_DIR = "logs"
SYSTEM_LOG_DIR = os.path.join(LOG_DIR, "system")
ERROR_LOG_DIR = os.path.join(LOG_DIR, "errors")
JSON_LOG_DIR = os.path.join(LOG_DIR, "json")
ROOMING_JSON_LOG_DIR = os.path.join(JSON_LOG_DIR, "rooming")
OCCUPATION_JSON_LOG_DIR = os.path.join(JSON_LOG_DIR, "occupation")

# Arquivos de log
SYSTEM_LOG_FILE = os.path.join(SYSTEM_LOG_DIR, "BI-HITS_system_log.txt")
UPDATED_DATA_LOG_FILE = os.path.join(SYSTEM_LOG_DIR, "BI-HITS_updated_data_log.txt")
ERROR_LOG_FILE = os.path.join(ERROR_LOG_DIR, "BI-HITS_error_log.txt")

# Arquivos JSON locais (cache)
LOCAL_ROOMING_JSON = os.path.join(JSON_LOG_DIR, "BI-HITS-ultimo_dado_rooming.json")
LOCAL_OCCUPATION_JSON = os.path.join(JSON_LOG_DIR, "BI-HITS-ultimo_dado_occupation.json")

# =============================================
# Funções para garantir diretórios
# =============================================
def ensure_dir(path):
    """Cria o diretório 'path' se não existir."""
    if not os.path.exists(path):
        os.makedirs(path)

def ensure_log_dir():
    """
    Garante a existência de toda a estrutura de pastas:
      logs/
        errors/
        system/
        json/
          rooming/
          occupation/
    """
    ensure_dir(LOG_DIR)
    ensure_dir(SYSTEM_LOG_DIR)
    ensure_dir(ERROR_LOG_DIR)
    ensure_dir(JSON_LOG_DIR)
    ensure_dir(ROOMING_JSON_LOG_DIR)
    ensure_dir(OCCUPATION_JSON_LOG_DIR)

# =============================================
# Funções de Log
# =============================================
def log_system(msg):
    """
    Registra mensagens gerais do sistema (inserts, updates, infos, etc.)
    no arquivo 'logs/system/BI-HITS_system_log.txt'.
    """
    ensure_log_dir()
    timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    full_msg = f"[{timestamp}] [SYSTEM] {msg}"
    print(full_msg)
    with open(SYSTEM_LOG_FILE, "a", encoding="utf-8") as log_file:
        log_file.write(full_msg + "\n")

def log_update_data(msg):
    """
    Registra detalhes de registros novos/atualizados no arquivo
    'logs/system/BI-HITS_updated_data_log.txt'.
    """
    ensure_log_dir()
    timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    full_msg = f"[{timestamp}] [UPDATE] {msg}"
    print(full_msg)
    with open(UPDATED_DATA_LOG_FILE, "a", encoding="utf-8") as log_file:
        log_file.write(full_msg + "\n")

def log_error(msg):
    """
    Registra falhas/erros no arquivo 'logs/errors/BI-HITS_error_log.txt'.
    """
    ensure_log_dir()
    timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    full_msg = f"[{timestamp}] [ERROR] {msg}"
    print(full_msg)
    with open(ERROR_LOG_FILE, "a", encoding="utf-8") as log_file:
        log_file.write(full_msg + "\n")


# =============================================
# Funções de Token e Fetch
# =============================================
def get_cached_token():
    cache_file = "token_cache.json"
    if os.path.exists(cache_file):
        with open(cache_file, "r", encoding="utf-8") as f:
            cache_data = json.load(f)
        if "token" in cache_data and "expires" in cache_data and cache_data["expires"] > time.time():
            log_system("Token recuperado do cache.")
            return cache_data["token"]
    token = get_token()
    if not token:
        raise Exception("Falha ao obter token.")
    with open(cache_file, "w", encoding="utf-8") as f:
        json.dump({"token": token, "expires": time.time() + 14400}, f)
    log_system("Token novo obtido e armazenado no cache.")
    return token

def get_token():
    token_url = "http://localhost/BI-HITS/dados/Hits/token/token.php"
    payload = {
        "username": "seu_usuario",  # Ajuste conforme necessário
        "password": "sua_senha"     # Ajuste conforme necessário
    }
    try:
        response = requests.post(token_url, data=payload, timeout=30)
        response.raise_for_status()
        raw = response.text
        log_system(f"Resposta bruta do token: {raw}")
        prefix = "Token obtido:"
        if raw.startswith(prefix):
            token = raw.replace(prefix, "").strip()
            return token
        else:
            data = response.json()
            return data.get("token")
    except Exception as e:
        log_error(f"Erro ao obter token: {e}")
        return None

def fetch_rooming_nights(page, headers, ini_date, fin_date, size, retries=3, timeout=60):
    url = "https://api.hitspms.net/Datashare/RevenueManagement/RoomingNights"
    params = {
        "IniDate": ini_date,
        "FinDate": fin_date,
        "Page": page,
        "Size": size
    }
    attempt = 0
    while attempt < retries:
        try:
            response = requests.get(url, headers=headers, params=params, timeout=timeout)
            response.raise_for_status()
            result = response.json()
            if isinstance(result, dict) and "data" in result:
                result = result["data"]
            return result
        except Exception as e:
            log_error(f"Erro na RoomingNights - Página {page} tentativa {attempt+1}: {e}")
            attempt += 1
            time.sleep(3)
    return []

def fetch_occupation_inventory(page, headers, ini_date, fin_date, size, retries=3, timeout=60):
    url = "https://api.hitspms.net/Datashare/RevenueManagement/OccupationInventory"
    params = {
        "IniDate": ini_date,
        "FinDate": fin_date,
        "Page": page,
        "Size": size
    }
    attempt = 0
    while attempt < retries:
        try:
            response = requests.get(url, headers=headers, params=params, timeout=timeout)
            response.raise_for_status()
            result = response.json()
            if isinstance(result, dict) and "data" in result:
                result = result["data"]
            return result
        except Exception as e:
            log_error(f"Erro na OccupationInventory - Página {page} tentativa {attempt+1}: {e}")
            attempt += 1
            time.sleep(3)
    return []

# =============================================
# Função para calcular diferenças entre registros
# =============================================
def get_differences(old_record, new_record):
    differences = []
    all_keys = set(old_record.keys()).union(new_record.keys())
    for key in all_keys:
        old_val = old_record.get(key)
        new_val = new_record.get(key)
        if old_val != new_val:
            differences.append(f"{key}: '{old_val}' -> '{new_val}'")
    return ", ".join(differences)

# =============================================
# Função de Comparação dos Dados
# =============================================
def compare_data(api_data, local_data, key_func):
    """
    Compara os dados da API com os dados locais.
    Retorna um dicionário com listas 'updates' e 'new'.
    Para registros atualizados, adiciona um campo 'diff' com as diferenças.
    """
    local_dict = {}
    for item in local_data:
        k = key_func(item)
        local_dict[k] = item
    updates = []
    new_records = []
    for record in api_data:
        k = key_func(record)
        # Define a chave única no registro com a chave composta
        record["id"] = k
        if k in local_dict:
            # Se houver diferença, gera diff
            if json.dumps(local_dict[k], sort_keys=True) != json.dumps(record, sort_keys=True):
                diff = get_differences(local_dict[k], record)
                record["diff"] = diff
                updates.append(record)
        else:
            new_records.append(record)
    return {"updates": updates, "new": new_records}

# =============================================
# Funções para obter chave única
# =============================================
def rooming_key(record):
    # Alteramos a chave única para ser composta por campos que não mudam para a mesma reserva
    return f"{record.get('propertyId')}_{record.get('endOfDayDate')}_{record.get('dailyType')}_{record.get('originNumber')}"

def occupation_key(record):
    return f"{record.get('propertyId')}_{record.get('date')}"

# =============================================
# Upsert para RoomingNights
# =============================================
def upsert_rooming_nights(records):
    try:
        conn = mysql.connector.connect(
            host="localhost",
            user="root",      # Substitua pelo seu usuário do MySQL
            password="",      # Substitua pela sua senha
            database="bihits"
        )
    except Exception as e:
        log_error(f"Erro ao conectar com MySQL (RoomingNights): {e}")
        return

    cursor = conn.cursor()
    
    sql = """
    INSERT INTO RoomingNights (
        id, propertyId, originId, type, roomId, pax, chd, chdAge1, chdAge2, chdAge3, chdAge4, age5,
        amount, endOfDayDate, date, dailyType, roomingNightCount, roomTypeId, version, requesterCity,
        companyId, companyName, companyTravelAgent, companyTravelAgentId, companyTravelAgentName,
        requesterCountry, guestId, guestName, guestMail, guestPhone, guestCellPhone, mealPlanType,
        mealPlanAmountPaxChd, qtyAgeNotDefined, qtyEightyOneToNinety, qtyElevenToFifteen, qtyFemale,
        qtyFiftyOneToSixty, qtyFortyOneToFifty, qtyFromZeroToTen, qtyGenderNotDefined, qtyGreaterThanNinety,
        qtyMale, qtySeventyOneToEighty, qtySixteenToTwenty, qtySixtyOneToSeventy, qtyThirtyOneToForty,
        qtyTwentyOneToThirty, rateId, rateName, ratePlanId, ratePlanName, requesterState, requesterId,
        requesterName, requesterTravelAgent, reservationChannelId, reservationChannelName, roomCode,
        roomTypeName, companyCity, companyCountry, companyState, guestCity, guestCountry, guestState,
        propertyName, managerId, managerName, marketSegmentId, marketSegmentName, purposeStay, purposeStayId,
        sourceChannelId, sourceChannelName, dateAdd, companyMainDoc, companyMainDocType, dailyMonth, dailyYear,
        `group`, groupName, guestMainDoc, guestMainDocType, originNumber, originType, requesterMainDoc,
        requesterMainDocType, reservationCreatedAt, reservationCreatedUser, reservationCreatedUserId,
        dateCheckin, dateCheckout, voucher, reservationStatus, reservationStatusName, feeAmount,
        chdRange1, chdRange2, chdRange3
    ) VALUES (
        %(id)s, %(propertyId)s, %(originId)s, %(type)s, %(roomId)s, %(pax)s, %(chd)s, %(chdAge1)s, %(chdAge2)s,
        %(chdAge3)s, %(chdAge4)s, %(age5)s, %(amount)s, %(endOfDayDate)s, %(date)s, %(dailyType)s,
        %(roomingNightCount)s, %(roomTypeId)s, %(version)s, %(requesterCity)s, %(companyId)s, %(companyName)s,
        %(companyTravelAgent)s, %(companyTravelAgentId)s, %(companyTravelAgentName)s, %(requesterCountry)s,
        %(guestId)s, %(guestName)s, %(guestMail)s, %(guestPhone)s, %(guestCellPhone)s, %(mealPlanType)s,
        %(mealPlanAmountPaxChd)s, %(qtyAgeNotDefined)s, %(qtyEightyOneToNinety)s, %(qtyElevenToFifteen)s,
        %(qtyFemale)s, %(qtyFiftyOneToSixty)s, %(qtyFortyOneToFifty)s, %(qtyFromZeroToTen)s, %(qtyGenderNotDefined)s,
        %(qtyGreaterThanNinety)s, %(qtyMale)s, %(qtySeventyOneToEighty)s, %(qtySixteenToTwenty)s,
        %(qtySixtyOneToSeventy)s, %(qtyThirtyOneToForty)s, %(qtyTwentyOneToThirty)s, %(rateId)s, %(rateName)s,
        %(ratePlanId)s, %(ratePlanName)s, %(requesterState)s, %(requesterId)s, %(requesterName)s,
        %(requesterTravelAgent)s, %(reservationChannelId)s, %(reservationChannelName)s, %(roomCode)s,
        %(roomTypeName)s, %(companyCity)s, %(companyCountry)s, %(companyState)s, %(guestCity)s,
        %(guestCountry)s, %(guestState)s, %(propertyName)s, %(managerId)s, %(managerName)s, %(marketSegmentId)s,
        %(marketSegmentName)s, %(purposeStay)s, %(purposeStayId)s, %(sourceChannelId)s, %(sourceChannelName)s,
        %(dateAdd)s, %(companyMainDoc)s, %(companyMainDocType)s, %(dailyMonth)s, %(dailyYear)s, %(group)s,
        %(groupName)s, %(guestMainDoc)s, %(guestMainDocType)s, %(originNumber)s, %(originType)s,
        %(requesterMainDoc)s, %(requesterMainDocType)s, %(reservationCreatedAt)s, %(reservationCreatedUser)s,
        %(reservationCreatedUserId)s, %(dateCheckin)s, %(dateCheckout)s, %(voucher)s, %(reservationStatus)s,
        %(reservationStatusName)s, %(feeAmount)s, %(chdRange1)s, %(chdRange2)s, %(chdRange3)s
    )
    ON DUPLICATE KEY UPDATE
        propertyId = VALUES(propertyId),
        originId = VALUES(originId),
        type = VALUES(type),
        roomId = VALUES(roomId),
        pax = VALUES(pax),
        chd = VALUES(chd),
        chdAge1 = VALUES(chdAge1),
        chdAge2 = VALUES(chdAge2),
        chdAge3 = VALUES(chdAge3),
        chdAge4 = VALUES(chdAge4),
        age5 = VALUES(age5),
        amount = VALUES(amount),
        endOfDayDate = VALUES(endOfDayDate),
        date = VALUES(date),
        dailyType = VALUES(dailyType),
        roomingNightCount = VALUES(roomingNightCount),
        roomTypeId = VALUES(roomTypeId),
        version = VALUES(version),
        requesterCity = VALUES(requesterCity),
        companyId = VALUES(companyId),
        companyName = VALUES(companyName),
        companyTravelAgent = VALUES(companyTravelAgent),
        companyTravelAgentId = VALUES(companyTravelAgentId),
        companyTravelAgentName = VALUES(companyTravelAgentName),
        requesterCountry = VALUES(requesterCountry),
        guestId = VALUES(guestId),
        guestName = VALUES(guestName),
        guestMail = VALUES(guestMail),
        guestPhone = VALUES(guestPhone),
        guestCellPhone = VALUES(guestCellPhone),
        mealPlanType = VALUES(mealPlanType),
        mealPlanAmountPaxChd = VALUES(mealPlanAmountPaxChd),
        qtyAgeNotDefined = VALUES(qtyAgeNotDefined),
        qtyEightyOneToNinety = VALUES(qtyEightyOneToNinety),
        qtyElevenToFifteen = VALUES(qtyElevenToFifteen),
        qtyFemale = VALUES(qtyFemale),
        qtyFiftyOneToSixty = VALUES(qtyFiftyOneToSixty),
        qtyFortyOneToFifty = VALUES(qtyFortyOneToFifty),
        qtyFromZeroToTen = VALUES(qtyFromZeroToTen),
        qtyGenderNotDefined = VALUES(qtyGenderNotDefined),
        qtyGreaterThanNinety = VALUES(qtyGreaterThanNinety),
        qtyMale = VALUES(qtyMale),
        qtySeventyOneToEighty = VALUES(qtySeventyOneToEighty),
        qtySixteenToTwenty = VALUES(qtySixteenToTwenty),
        qtySixtyOneToSeventy = VALUES(qtySixtyOneToSeventy),
        qtyThirtyOneToForty = VALUES(qtyThirtyOneToForty),
        qtyTwentyOneToThirty = VALUES(qtyTwentyOneToThirty),
        rateId = VALUES(rateId),
        rateName = VALUES(rateName),
        ratePlanId = VALUES(ratePlanId),
        ratePlanName = VALUES(ratePlanName),
        requesterState = VALUES(requesterState),
        requesterId = VALUES(requesterId),
        requesterName = VALUES(requesterName),
        requesterTravelAgent = VALUES(requesterTravelAgent),
        reservationChannelId = VALUES(reservationChannelId),
        reservationChannelName = VALUES(reservationChannelName),
        roomCode = VALUES(roomCode),
        roomTypeName = VALUES(roomTypeName),
        companyCity = VALUES(companyCity),
        companyCountry = VALUES(companyCountry),
        companyState = VALUES(companyState),
        guestCity = VALUES(guestCity),
        guestCountry = VALUES(guestCountry),
        guestState = VALUES(guestState),
        propertyName = VALUES(propertyName),
        managerId = VALUES(managerId),
        managerName = VALUES(managerName),
        marketSegmentId = VALUES(marketSegmentId),
        marketSegmentName = VALUES(marketSegmentName),
        purposeStay = VALUES(purposeStay),
        purposeStayId = VALUES(purposeStayId),
        sourceChannelId = VALUES(sourceChannelId),
        sourceChannelName = VALUES(sourceChannelName),
        dateAdd = VALUES(dateAdd),
        companyMainDoc = VALUES(companyMainDoc),
        companyMainDocType = VALUES(companyMainDocType),
        dailyMonth = VALUES(dailyMonth),
        dailyYear = VALUES(dailyYear),
        `group` = VALUES(`group`),
        groupName = VALUES(groupName),
        guestMainDoc = VALUES(guestMainDoc),
        guestMainDocType = VALUES(guestMainDocType),
        originNumber = VALUES(originNumber),
        originType = VALUES(originType),
        requesterMainDoc = VALUES(requesterMainDoc),
        requesterMainDocType = VALUES(requesterMainDocType),
        reservationCreatedAt = VALUES(reservationCreatedAt),
        reservationCreatedUser = VALUES(reservationCreatedUser),
        reservationCreatedUserId = VALUES(reservationCreatedUserId),
        dateCheckin = VALUES(dateCheckin),
        dateCheckout = VALUES(dateCheckout),
        voucher = VALUES(voucher),
        reservationStatus = VALUES(reservationStatus),
        reservationStatusName = VALUES(reservationStatusName),
        feeAmount = VALUES(feeAmount),
        chdRange1 = VALUES(chdRange1),
        chdRange2 = VALUES(chdRange2),
        chdRange3 = VALUES(chdRange3)
    """
    
    for record in records:
        try:
            cursor.execute(sql, record)
            log_system(f"Upsert RoomingNight (ID: {record.get('id')}) OK")
        except Exception as e:
            log_error(f"Erro ao processar RoomingNight (ID: {record.get('id')}): {e}")
    
    conn.commit()
    cursor.close()
    conn.close()
    log_system("Upsert RoomingNights concluído. Registros processados: " + str(len(records)))

# =============================================
# Upsert para OccupationInventory e RoomTypes
# =============================================
def upsert_occupation_inventory(records):
    try:
        conn = mysql.connector.connect(
            host="localhost",
            user="root",       # Substitua pelo seu usuário do MySQL
            password="",       # Substitua pela sua senha
            database="bihits"
        )
    except Exception as e:
        log_error(f"Erro ao conectar com o MySQL (OccupationInventory): {e}")
        return

    cursor = conn.cursor()

    inv_sql = """
    INSERT INTO OccupationInventory (propertyId, date, totalRoom, occ, outOfOrder, avail)
    VALUES (%(propertyId)s, %(date)s, %(totalRoom)s, %(occ)s, %(outOfOrder)s, %(avail)s)
    ON DUPLICATE KEY UPDATE
        totalRoom = VALUES(totalRoom),
        occ = VALUES(occ),
        outOfOrder = VALUES(outOfOrder),
        avail = VALUES(avail),
        id = LAST_INSERT_ID(id)
    """
    room_sql = """
    INSERT INTO OccupationInventoryRoomTypes (
        occupationInventoryId, roomTypeId, roomTypeName, totalRoomType, occRoomType, availRoomType, outOfOrderRoomType
    ) VALUES (
        %(occupationInventoryId)s, %(roomTypeId)s, %(roomTypeName)s, %(totalRoomType)s, %(occRoomType)s, %(availRoomType)s, %(outOfOrderRoomType)s
    )
    ON DUPLICATE KEY UPDATE
        roomTypeName = VALUES(roomTypeName),
        totalRoomType = VALUES(totalRoomType),
        occRoomType = VALUES(occRoomType),
        availRoomType = VALUES(availRoomType),
        outOfOrderRoomType = VALUES(outOfOrderRoomType)
    """
    processed_count = 0
    for record in records:
        inv_data = {
            "propertyId": record.get("propertyId"),
            "date": record.get("date"),
            "totalRoom": record.get("totalRoom"),
            "occ": record.get("occ"),
            "outOfOrder": record.get("outOfOrder"),
            "avail": record.get("avail")
        }
        try:
            cursor.execute(inv_sql, inv_data)
            inv_id = cursor.lastrowid
            log_system(f"Upsert OccupationInventory (propertyId: {record.get('propertyId')}, date: {record.get('date')}) -> ID: {inv_id}")
        except Exception as e:
            log_error(f"Erro no upsert de OccupationInventory (propertyId: {record.get('propertyId')}, date: {record.get('date')}): {e}")
            continue

        room_types = record.get("roomTypes", [])
        for rt in room_types:
            rt_data = {
                "occupationInventoryId": inv_id,
                "roomTypeId": rt.get("roomTypeId"),
                "roomTypeName": rt.get("roomTypeName"),
                "totalRoomType": rt.get("totalRoomType"),
                "occRoomType": rt.get("occRoomType"),
                "availRoomType": rt.get("availRoomType"),
                "outOfOrderRoomType": rt.get("outOfOrderRoomType")
            }
            try:
                cursor.execute(room_sql, rt_data)
                log_system(f"Upsert OccupationInventoryRoomTypes -> InventoryID {inv_id}, roomTypeId {rt.get('roomTypeId')}")
            except Exception as e:
                log_error(f"Erro no upsert de RoomType (InvID {inv_id}, roomTypeId {rt.get('roomTypeId')}): {e}")
        processed_count += 1

    conn.commit()
    cursor.close()
    conn.close()
    log_system("Upsert OccupationInventory concluído. Registros processados: " + str(processed_count))

# =============================================
# Salvando todos os novos/atualizados em um único arquivo JSON
# =============================================
def save_all_new_updated_json(new_records, updated_records, category):
    """
    Salva todos os registros novos e atualizados em um único arquivo JSON 
    (ao invés de criar um arquivo para cada registro).
    O arquivo será criado apenas se houver registros.
    
    Exemplo de nome de arquivo gerado para 'rooming':
      logs/json/rooming/BI-HITS-rooming_updates_20230329_192827.json
    """
    if not new_records and not updated_records:
        return  # nada para salvar

    ensure_log_dir()  # garante que as pastas existem

    # Monta o dicionário final
    data_to_save = {
        "new_records": new_records,
        "updated_records": updated_records
    }
    # Nome do arquivo, ex: "BI-HITS-rooming_updates_YYYYMMDD_HHMMSS.json"
    timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
    filename = f"BI-HITS-{category}_updates_{timestamp}.json"

    # Decide se é "rooming" ou "occupation" para gravar na subpasta correta
    if category == "rooming":
        full_path = os.path.join(ROOMING_JSON_LOG_DIR, filename)
    elif category == "occupation":
        full_path = os.path.join(OCCUPATION_JSON_LOG_DIR, filename)
    else:
        # fallback: grava em logs/json
        full_path = os.path.join(JSON_LOG_DIR, filename)

    try:
        with open(full_path, "w", encoding="utf-8") as f:
            json.dump(data_to_save, f, ensure_ascii=False, indent=4)
        log_system(f"Arquivo JSON gerado para {category}: {full_path}")
    except Exception as e:
        log_error(f"Erro ao salvar JSON de {category}: {e}")

# =============================================
# Processamento de RoomingNights
# =============================================
def process_rooming_nights():
    ini_date = "2024-01-01T00:00:00"
    fin_date = "2026-12-31T23:59:59"
    size = 50
    page = 0
    all_data = []
    
    log_system("Iniciando busca de dados da API (RoomingNights).")
    token = get_cached_token()
    headers = {
        "X-API-VERSION": "1",
        "X-API-TENANT-NAME": "thecoralbeachresort",
        "X-API-PROPERTY-CODE": "1",
        "X-API-PARTNER-USERID": "0",
        "X-API-LANGUAGE-CODE": "pt-br",
        "X-Client-Id": "THECORALBR",
        "Authorization": f"Bearer {token}"
    }
    while True:
        data = fetch_rooming_nights(page, headers, ini_date, fin_date, size)
        if not isinstance(data, list):
            log_error(f"RoomingNights - Página {page} com estrutura inesperada: {data}")
            break
        count = len(data)
        log_system(f"RoomingNights - [Página {page}] {count} registros carregados.")
        if count == 0:
            break
        all_data.extend(data)
        if count < size:
            break
        page += 1
    log_system(f"RoomingNights - Total de registros obtidos da API: {len(all_data)}")
    
    # Usaremos o arquivo local no logs/json
    local_file = LOCAL_ROOMING_JSON
    
    # Carregar dados locais
    if os.path.exists(local_file):
        try:
            with open(local_file, "r", encoding="utf-8") as f:
                local_data = json.load(f)
            log_system(f"RoomingNights - Dados carregados do arquivo local. Total: {len(local_data)} registros.")
        except Exception as e:
            log_error(f"RoomingNights - Erro ao carregar arquivo local: {e}")
            local_data = []
    else:
        log_system("RoomingNights - Arquivo local não encontrado. Dados vazios para comparação.")
        local_data = []
    
    # Comparar
    comparison = compare_data(all_data, local_data, rooming_key)
    new_records = comparison["new"]
    updates = comparison["updates"]
    log_system(f"RoomingNights - Registros novos: {len(new_records)}; Registros atualizados: {len(updates)}")
    
    # Logs detalhados de novos/atualizados
    if new_records or updates:
        for record in new_records:
            company = record.get("companyName", "Cliente não informado")
            msg = f"RoomingNight novo: ID {record.get('id')}, Cliente: {company}"
            log_update_data(msg)
        for record in updates:
            company = record.get("companyName", "Cliente não informado")
            diff = record.get("diff", "")
            msg = f"RoomingNight atualizado: ID {record.get('id')}, Cliente: {company}. Alterações: {diff}"
            log_update_data(msg)
    else:
        log_update_data("RoomingNights - Nenhum dado atualizado")
    
    # Salva JSON de updates (opcional)
    save_all_new_updated_json(new_records, updates, "rooming")
    
    records_to_process = new_records + updates
    if records_to_process:
        upsert_rooming_nights(records_to_process)
    else:
        log_system("RoomingNights - Nenhum registro novo/atualizado para upsert.")
    
    # Atualiza arquivo local
    try:
        with open(local_file, "w", encoding="utf-8") as f:
            json.dump(all_data, f, ensure_ascii=False, indent=4)
        log_system("RoomingNights - Arquivo local atualizado com os dados da API.")
    except Exception as e:
        log_error(f"RoomingNights - Erro ao atualizar arquivo local: {e}")

# =============================================
# Processamento de OccupationInventory
# =============================================
def fetch_all_occupation_inventory(headers, ini_date, fin_date, page_size=50):
    all_data = []
    page = 0
    while True:
        data = fetch_occupation_inventory(page, headers, ini_date, fin_date, page_size)
        if not isinstance(data, list):
            log_error(f"OccupationInventory - Página {page} retornou estrutura inesperada: {data}")
            break
        count = len(data)
        log_system(f"OccupationInventory - Página {page} retornou {count} registros.")
        if count == 0:
            break
        all_data.extend(data)
        page += 1
    return all_data

def process_occupation_inventory():
    ini_date = "2024-01-01T00:00:00"
    fin_date = "2026-12-31T23:59:59"
    size = 50
    log_system("Iniciando busca de dados da API (OccupationInventory).")
    token = get_cached_token()
    headers = {
        "X-API-VERSION": "1",
        "X-API-TENANT-NAME": "thecoralbeachresort",
        "X-API-PROPERTY-CODE": "1",
        "X-API-PARTNER-USERID": "0",
        "X-API-LANGUAGE-CODE": "pt-br",
        "X-Client-Id": "THECORALBR",
        "Authorization": f"Bearer {token}"
    }
    
    all_data = fetch_all_occupation_inventory(headers, ini_date, fin_date, size)
    log_system(f"OccupationInventory - Total de registros obtidos da API: {len(all_data)}")
    
    # Arquivo local no logs/json
    local_file = LOCAL_OCCUPATION_JSON
    
    if os.path.exists(local_file):
        try:
            with open(local_file, "r", encoding="utf-8") as f:
                local_data = json.load(f)
            log_system(f"OccupationInventory - Dados carregados do arquivo local. Total: {len(local_data)} registros.")
        except Exception as e:
            log_error(f"OccupationInventory - Erro ao carregar arquivo local: {e}")
            local_data = []
    else:
        log_system("OccupationInventory - Arquivo local não encontrado. Dados vazios para comparação.")
        local_data = []
    
    comparison = compare_data(all_data, local_data, occupation_key)
    new_records = comparison["new"]
    updates = comparison["updates"]
    log_system(f"OccupationInventory - Registros novos: {len(new_records)}; Registros atualizados: {len(updates)}")
    
    # Logs detalhados
    if new_records or updates:
        for record in new_records:
            prop = record.get("propertyId", "N/A")
            msg = f"OccupationInventory novo: {occupation_key(record)} (Property: {prop})"
            log_update_data(msg)
        for record in updates:
            prop = record.get("propertyId", "N/A")
            diff = record.get("diff", "")
            msg = f"OccupationInventory atualizado: {occupation_key(record)} (Property: {prop}). Alterações: {diff}"
            log_update_data(msg)
    else:
        log_update_data("OccupationInventory - Nenhum dado atualizado")
    
    # Salva JSON de updates (opcional)
    save_all_new_updated_json(new_records, updates, "occupation")
    
    records_to_process = new_records + updates
    if records_to_process:
        upsert_occupation_inventory(records_to_process)
    else:
        log_system("OccupationInventory - Nenhum registro novo/atualizado para upsert.")
    
    try:
        with open(local_file, "w", encoding="utf-8") as f:
            json.dump(all_data, f, ensure_ascii=False, indent=4)
        log_system("OccupationInventory - Arquivo local atualizado com os dados da API.")
    except Exception as e:
        log_error(f"OccupationInventory - Erro ao atualizar arquivo local: {e}")

# =============================================
# Main
# =============================================
def main():
    process_rooming_nights()
    process_occupation_inventory()

if __name__ == '__main__':
    main()
