import requests
import json
import time
import threading
from apscheduler.schedulers.background import BackgroundScheduler

# -------------------------------
# Core Heartbeat Functionality
# -------------------------------

# Pulse control globals
url = ""
variable_parameter = True
pulse_thread = None

# 1. Configure pulse URL
def setUrl(url_):
    '''
    Define the endpoint for sending heartbeats.
    '''
    
    global url
    url = url_

# 2. Send a single pulse
def sendPulse(name, description, additional_info, show_response=False, api_token=None):
    '''
    Send a heartbeat pulse to the configured URL.
    '''
    
    payload = {
        "processName": name,
        "processDescription": description,
        "additionalData": additional_info
    }
    headers = {'Content-Type': 'application/json'}
    if api_token:
        headers['Authorization'] = f"Bearer {api_token}"
    try:
        response = requests.post(url, json=payload, headers=headers)
        if show_response:
            print(f">>> Heartbeat response: {response.status_code}")
    except Exception as e:
        print('*** Error sending pulse! *** >', e)

# 3. Thread loop sending pulses at fixed intervals
def pulse(interval, name, description, additional_info, show_response, show_logs, api_token):
    '''
    Background thread that sends pulses at the specified interval.
    '''
    
    if show_logs:
        print('>>> Heartbeat thread has started. <<<')
    while variable_parameter:
        sendPulse(name, description, additional_info, show_response, api_token)
        # wait interval seconds, waking up each second to check kill flag
        for _ in range(interval):
            if not variable_parameter:
                break
            time.sleep(1)

# 4. Start heartbeat (legacy, no business hours enforcement)
def heartbeat(
    interval=600,
    name='',
    description='',
    additional_info='',
    show_response=False,
    show_logs=False,
    api_token=None
):
    '''
    Begin sending pulses on a background thread at the specified interval.
    '''
    
    global variable_parameter, pulse_thread
    variable_parameter = True
    # avoid overlapping thread
    if pulse_thread is not None:
        pulse_thread.join(timeout=1.0)
    # start new thread
    pulse_thread = threading.Thread(
        target=pulse,
        args=(interval, name, description, additional_info, show_response, show_logs, api_token)
    )
    pulse_thread.start()

# 5. Kill heartbeat thread (with optional scheduler shutdown)
def killHeartbeat(disable_schedule: bool = True):
    '''
    Stop the heartbeat thread. If disable_schedule=True, also shutdown business-hour scheduler.
    '''
    
    global variable_parameter, pulse_thread, _scheduler
    variable_parameter = False
    time.sleep(1)
    if pulse_thread and pulse_thread.is_alive():
        print('!!! Heartbeat thread is still running. !!!')
    else:
        print('>>> Heartbeat thread has ended. <<<')
    # optionally shutdown scheduler
    if disable_schedule and _scheduler:
        _scheduler.shutdown(wait=False)
        _scheduler = None
        print('>>> Scheduler also shut down; no auto-restart. <<<')

# -------------------------------
# Scheduler for Business Hours
# -------------------------------

# Internal scheduler and config defaults
_scheduler = None
_start_hour = 9
_end_hour = 18
_days = 'mon-fri'
_tz = 'America/Sao_Paulo'

# 6. Runtime business hours config
def setBusinessHours(start_hour: int, end_hour: int, days: str = 'mon-fri', tz: str = 'America/Sao_Paulo'):
    '''
    Configure business hours window and days for scheduled heartbeat.
    '''
    
    global _start_hour, _end_hour, _days, _tz
    _start_hour = start_hour
    _end_hour = end_hour
    _days = days
    _tz = tz

# 7. Start heartbeat automatically on business hours
def businessHeartbeat(
    interval=600,
    name='',
    description='',
    additional_info='',
    show_response=False,
    show_logs=False,
    api_token=None
):
    '''
    Schedule heartbeat to start at _start_hour and stop at _end_hour on configured days.
    '''
    
    global _scheduler
    # initialize or clear scheduler
    if _scheduler:
        _scheduler.remove_all_jobs()
    else:
        _scheduler = BackgroundScheduler(timezone=_tz)
    # schedule start
    _scheduler.add_job(
        lambda: heartbeat(
            interval, name, description, additional_info,
            show_response, show_logs, api_token
        ),
        'cron', day_of_week=_days, hour=_start_hour, minute=0
    )
    # schedule stop
    _scheduler.add_job(
        lambda: killHeartbeat(disable_schedule=False),
        'cron', day_of_week=_days, hour=_end_hour, minute=0
    )
    _scheduler.start()
