import requests
import json
import time
import os
import mysql.connector
from datetime import datetime

# =============================================
# Constantes de caminhos
# =============================================
LOG_DIR = "logs"  # Pasta de logs permanece para registrar mensagens
SYSTEM_LOG_DIR = os.path.join(LOG_DIR, "system")
ERROR_LOG_DIR = os.path.join(LOG_DIR, "errors")

# Nova pasta para cache dos dados (Full Load / Incremental)
CACHE_DIR = "dados"
ROOMING_CACHE_FILE = os.path.join(CACHE_DIR, "BI-HITS-ultimo_dado_rooming.json")
OCCUPATION_CACHE_FILE = os.path.join(CACHE_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 da estrutura de pastas para os logs:
      logs/
        errors/
        system/
    """
    ensure_dir(LOG_DIR)
    ensure_dir(SYSTEM_LOG_DIR)
    ensure_dir(ERROR_LOG_DIR)

# =============================================
# Funções de Log
# =============================================
def log_system(msg):
    """Registra mensagens gerais do sistema no arquivo de log."""
    ensure_log_dir()
    timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    full_msg = f"[{timestamp}] [SYSTEM] {msg}"
    print(full_msg)
    with open(os.path.join(SYSTEM_LOG_DIR, "BI-HITS_system_log.txt"), "a", encoding="utf-8") as log_file:
        log_file.write(full_msg + "\n")

def log_update_data(msg):
    """Registra detalhes de registros novos/atualizados."""
    ensure_log_dir()
    timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    full_msg = f"[{timestamp}] [UPDATE] {msg}"
    print(full_msg)
    with open(os.path.join(SYSTEM_LOG_DIR, "BI-HITS_updated_data_log.txt"), "a", encoding="utf-8") as log_file:
        log_file.write(full_msg + "\n")

def log_error(msg):
    """Registra erros no arquivo de log."""
    ensure_log_dir()
    timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    full_msg = f"[{timestamp}] [ERROR] {msg}"
    print(full_msg)
    with open(os.path.join(ERROR_LOG_DIR, "BI-HITS_error_log.txt"), "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)
        record["id"] = k  # Define a chave única no registro
        if k in local_dict:
            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):
    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')}"

# =============================================
# Funções para truncar (apagar) os dados do banco
# =============================================
def truncate_rooming_nights():
    try:
        conn = mysql.connector.connect(host="localhost", user="root", password="", database="bihits")
        cursor = conn.cursor()
        cursor.execute("TRUNCATE TABLE RoomingNights")
        conn.commit()
        log_system("RoomingNights - Tabela truncada com sucesso.")
    except Exception as e:
        log_error(f"Erro truncando RoomingNights: {e}")
    finally:
        cursor.close()
        conn.close()

def truncate_occupation_inventory():
    try:
        conn = mysql.connector.connect(host="localhost", user="root", password="", database="bihits")
        cursor = conn.cursor()
        # Trunca primeiro a tabela dependente
        cursor.execute("TRUNCATE TABLE OccupationInventoryRoomTypes")
        cursor.execute("TRUNCATE TABLE OccupationInventory")
        conn.commit()
        log_system("OccupationInventory e OccupationInventoryRoomTypes - Tabelas truncadas com sucesso.")
    except Exception as e:
        log_error(f"Erro truncando OccupationInventory: {e}")
    finally:
        cursor.close()
        conn.close()

# =============================================
# Upsert para RoomingNights
# =============================================
def upsert_rooming_nights(records):
    try:
        conn = mysql.connector.connect(host="localhost", user="root", password="", 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", password="", 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 os registros em um arquivo de cache (JSON)
# =============================================
def save_cache_file(data, cache_file, category):
    try:
        ensure_dir(CACHE_DIR)
        with open(cache_file, "w", encoding="utf-8") as f:
            json.dump(data, f, ensure_ascii=False, indent=4)
        log_system(f"{category} - Cache criado/atualizado: {cache_file}")
    except Exception as e:
        log_error(f"Erro ao salvar cache 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

    # Verifica se a pasta de cache existe antes de chamar a API
    if not os.path.exists(CACHE_DIR):
        log_system("Pasta de dados não encontrada. Será executada carga completa (Full Load) para RoomingNights.")
        full_load = True
    else:
        full_load = False
        log_system("Pasta de dados encontrada. Executando carga incremental para 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}"
    }
    
    # Busca todos os registros da API
    all_data = []
    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)}")
    
    if not os.path.exists(ROOMING_CACHE_FILE):
        log_system("Arquivo de cache de RoomingNights não encontrado. Executando carga completa (Full Load).")
        truncate_rooming_nights()
        upsert_rooming_nights(all_data)
        ensure_dir(CACHE_DIR)
        save_cache_file(all_data, ROOMING_CACHE_FILE, "RoomingNights")
        return

    # Caso exista o cache, executa carga incremental
    try:
        with open(ROOMING_CACHE_FILE, "r", encoding="utf-8") as f:
            local_data = json.load(f)
        log_system(f"RoomingNights - Cache carregado. Registros: {len(local_data)}")
    except Exception as e:
        log_error(f"RoomingNights - Erro ao carregar cache: {e}")
        local_data = []

    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)}; atualizados: {len(updates)}")
    
    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")
    
    save_cache_file(all_data, ROOMING_CACHE_FILE, "RoomingNights")
    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.")

# =============================================
# Processamento de OccupationInventory
# =============================================
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 = []
    page = 0
    while True:
        data = fetch_occupation_inventory(page, headers, ini_date, fin_date, size)
        if not isinstance(data, list):
            log_error(f"OccupationInventory - Página {page} com estrutura inesperada: {data}")
            break
        count = len(data)
        log_system(f"OccupationInventory - [Página {page}] {count} registros carregados.")
        if count == 0:
            break
        all_data.extend(data)
        if count < size:
            break
        page += 1
    log_system(f"OccupationInventory - Total de registros obtidos da API: {len(all_data)}")
    
    # Se a pasta de cache "dados" não existir, executa carga completa para OccupationInventory
    if not os.path.exists(CACHE_DIR):
        log_system("Pasta de dados não encontrada. Executando carga completa para OccupationInventory: apagando dados do banco e inserindo todos os registros da API.")
        truncate_occupation_inventory()
        upsert_occupation_inventory(all_data)
        ensure_dir(CACHE_DIR)
        save_cache_file(all_data, OCCUPATION_CACHE_FILE, "OccupationInventory")
        return
    
    # Caso a pasta exista, carrega o arquivo de cache (se existir) para comparação
    if os.path.exists(OCCUPATION_CACHE_FILE):
        try:
            with open(OCCUPATION_CACHE_FILE, "r", encoding="utf-8") as f:
                local_data = json.load(f)
            log_system(f"OccupationInventory - Cache carregado. Registros: {len(local_data)}")
        except Exception as e:
            log_error(f"OccupationInventory - Erro ao carregar cache: {e}")
            local_data = []
    else:
        log_system("OccupationInventory - Cache não encontrado. Considerando carga completa incremental.")
        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)}; atualizados: {len(updates)}")
    
    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")
    
    save_cache_file(all_data, OCCUPATION_CACHE_FILE, "OccupationInventory")
    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.")

# =============================================
# Main
# =============================================
def main():
    process_rooming_nights()
    process_occupation_inventory()

if __name__ == '__main__':
    main()
