from googleapiclient.discovery import build
from google.oauth2 import service_account
from datetime import datetime, timedelta
import pytz
from zoneinfo import ZoneInfo

import app_settings


class GoogleCalendarClient:
    def __init__(self, service_account_file: str, calendar_id: str, timezone: str = "Europe/Zagreb"):
        """
        Inicijalizacija Google Calendar klijenta.

        :param service_account_file: Putanja do JSON service account ključa
        :param calendar_id: ID kalendara (npr. 'primary' ili 'fitandmore-calendar@...gserviceaccount.com')
        :param timezone: Naziv vremenske zone (default: Europe/Zagreb)
        """
        self.service_account_file = service_account_file
        self.calendar_id = calendar_id
        self.timezone = timezone
        self.service = self._build_service()

    def _build_service(self):
        """Privatna metoda za autentifikaciju i inicijalizaciju Google Calendar API servisa."""
        #SCOPES = ['https://www.googleapis.com/auth/calendar.readonly']
        SCOPES = ['https://www.googleapis.com/auth/calendar']
        creds = service_account.Credentials.from_service_account_file(
            self.service_account_file, scopes=SCOPES
        )
        service = build('calendar', 'v3', credentials=creds)
        return service
    
    def get_rule_id_for_user(self, email: str):
        #Vrati rule_id (ACL id) za određenog korisnika ako postoji.
        acl_items = self.service.acl().list(calendarId=self.calendar_id).execute().get('items', [])
        for rule in acl_items:
            if rule.get('id') == f"user:{email}":
                return rule['id']
            scope = rule.get('scope', {})
            if scope.get('type') == 'user' and scope.get('value') == email:
                return rule['id']
        return None

    def list_calendars(self): # NOT USED
        """Vrati listu svih kalendara dostupnih servisnom računu."""
        result = self.service.calendarList().list().execute()
        return result.get('items', [])
    
    def share_check(self, calendar_id, user_account):
        # === Dohvati ACL listu ===
        acl = self.service.acl().list(calendarId=calendar_id).execute()

        print("Trenutna ACL pravila:")
        for rule in acl.get('items', []):
            print(f"- {rule['id']} ({rule['role']})")
            if(user_account == rule['scope']['value']):
                return True
        
        return False
    
    def share_calendar(self, calendar_id, google_account):
        """ SCOPES = ['https://www.googleapis.com/auth/calendar']
        credentials = service_account.Credentials.from_service_account_file(
            SERVICE_ACCOUNT_FILE, scopes=SCOPES
            )

        service = build('calendar', 'v3', credentials=credentials) """
        # Podijeli kalendar s google userom
        rule = {
            'scope': {
                'type': 'user',
                'value': google_account #'dinomand72@gmail.com'  # <-- zamijeni svojim emailom
            },
            'role': 'owner'  # ili 'writer' ako želiš samo uređivanje
        }

        created_rule = self.service.acl().insert(calendarId=calendar_id, body=rule).execute()
        print('Kalendar podijeljen s:', created_rule['id'])
        return True
    
    def share_delete(self, calendar_id,user_account):
        try:
            # Dohvati ACL listu (sve koji imaju pristup kalendaru)
            acl = self.service.acl().list(calendarId=calendar_id).execute()
            rule_id = None
            rule_id = self.get_rule_id_for_user(user_account)
            if rule_id:
                self.service.acl().delete(calendarId=calendar_id, ruleId=rule_id).execute()
                print(f"Obrisano dijeljenje za " + user_account)
                return True
            else:
                print("Nema takvog dijeljenja.")
        except Exception as e:
            print("Greška pri brisanju ACL pravila:", e)
        return False
    
    #*********************************
    # EVENT
    def add_event(self, summary, start_time, service_id, description: str = None, location: str = None):
        # Create event in calendar
        ret = {}
        ret['error'] = False
        ret['event'] = {}
        try:
            #start_time = datetime(2025, 11, 13, 14, 0)  # 2025-11-12 14:00
            block_start = start_time
            service_id = int(service_id)

            termin_duration = float(app_settings.termin_duration_h.get(service_id, 0))
            clean_duration = float(app_settings.clean_duration_h.get(service_id, 0))
            duration_total = termin_duration + clean_duration
            block_start = datetime.strptime(block_start, "%Y-%m-%d %H:%M")
            block_end = block_start + timedelta(hours=duration_total)
            # summary = "Title"

            event = {
                'summary': summary,
                'description': description,
                'location': location,
                'start': {'dateTime': block_start.isoformat(), 'timeZone': self.timezone},
                'end': {'dateTime': block_end.isoformat(), 'timeZone': self.timezone},
            }
            calendarID=app_settings.calendar_id[service_id] #CALENDAR_ID
            event_result = self.service.events().insert(calendarId=calendarID, body=event).execute()
            #print(f"✅ Event kreiran: {event_result.get('htmlLink')}")
            ret['event'] = event_result
        except Exception as e:
            #print(f"⚠️ Greška pri kreiranju događaja: {e}")
            ret['error'] = str(e)
        
        return ret
    
    def update_event_status(self, event_id, new_status):

        updated_event = self.service.events().patch(
            calendarId=self.calendar_id,
            eventId=event_id,
            body={"location": new_status}
        ).execute()

        return updated_event
    
    def get_event_location(self, event_id):
        event = self.service.events().get(
            calendarId=self.calendar_id,
            eventId=event_id
        ).execute()

        # Provjera location polja
        location = event.get("location")
        return location
    
    def get_events_OLD(self, days_ahead: int = 7, days_back: int = 0):
        """
        Dohvaća sve evente u zadanom vremenskom intervalu.

        :param days_ahead: Koliko dana unaprijed čitati (default 7)
        :param days_back: Koliko dana unatrag čitati (default 0)
        :return: Lista eventova (dict objekti iz Google API-ja)
        """
        tz = pytz.timezone(self.timezone)
        now = datetime.now(tz)
        time_min = (now - timedelta(days=days_back)).isoformat()
        time_max = (now + timedelta(days=days_ahead)).isoformat()

        events_result = self.service.events().list(
            calendarId=self.calendar_id,
            timeMin=time_min,
            timeMax=time_max,
            singleEvents=True,
            orderBy='startTime'
        ).execute()

        return events_result.get('items', [])
    
    
    def get_events_in_range(self, service_id:int, first_day: int=0, days_ahead: int = 15):
        """
        Dohvaća sve evente iz Google kalendara u idućih `days_ahead` dana.
        """
        #from datetime import datetime, timedelta, timezone
        ret = {}
        ret['error'] = None
        try:
            # Početak (danas)
            tz = pytz.timezone(self.timezone)

            now = datetime.now(tz).replace(hour=8, minute=0, second=0, microsecond=0) + timedelta(days=first_day)
            time_min = now.isoformat()
            time_max = (datetime.now(tz).replace(hour=20, minute=0, second=0, microsecond=1) + timedelta(days=days_ahead)).isoformat()

            #time_min = datetime.now().isoformat()

            # Kraj (danas + 15 dana)
            #time_max = (datetime.now() + timedelta(days=days_ahead)).isoformat()

            # Poziv Google API-ja
            events_result = self.service.events().list(
                calendarId=self.calendar_id,     # ili 'primary'
                timeMin=time_min,
                timeMax=time_max,
                singleEvents=True,
                orderBy='startTime'
            ).execute()

            events = events_result.get('items', [])

            if not events:
                #print("ℹ️ Nema događaja u tom periodu.")
                ret['events'] = []
                return ret

            #print(f"📅 Pronađeno {len(events)} događaja u sljedećih {days_ahead} dana:")
            events_new = []
            from dateutil import parser
            for event in events:
                # Uzmemo start i end vremena
                start_str = event['start'].get('dateTime', event['start'].get('date'))
                end_str = event['end'].get('dateTime', event['end'].get('date'))

                # Parsiramo string u datetime objekt (automatski detektira vremensku zonu)
                start_dt = parser.isoparse(start_str)
                end_dt = parser.isoparse(end_str)

                # Oduzmemo 30 minuta od početka (dakle, novi start je 30 minuta ranije)
                #new_start_dt = start_dt - timedelta(minutes=30)
                time_before = app_settings.clean_duration_h[service_id]
                new_start_dt = start_dt - timedelta(hours=time_before)

                # Pretvorimo nazad u ISO format (zadrži vremensku zonu ako postoji)
                new_start_iso = new_start_dt.isoformat()

                # Zamijenimo stari start novim
                event['start']['dateTime'] = new_start_iso

                # Dodamo ažurirani event u novu listu
                events_new.append(event)
            """ for event in events:
                # TODO - add CLEANING time before every event
                start = event['start'].get('dateTime', event['start'].get('date'))
                new_start = start #self.add_cleaning_time(start)
                event['start']['dateTime'] = new_start
                events_new.append(event) """
            
            ret['events'] = events_new
            return ret

        except Exception as e:
            #print(f"⚠️ Greška pri dohvaćanju događaja: {e}")
            ret['error'] = "⚠️ Greška pri dohvaćanju događaja: {e}"
            return ret
    
    def delete_event_by_id(self, event_id):
        ret = {}
        ret['error'] = None
        try:
            self.service.events().delete(
                calendarId=self.calendar_id,  # npr. 'primary' ili tvoj CALENDAR_ID
                eventId=event_id
            ).execute()
            #print("✅ Event uspješno obrisan.")
            return ret
        except Exception as e:
            #print(f"⚠️ Greška pri brisanju eventa: {e}")
            ret['error'] = str(e)
            return ret
        
    #from datetime import timedelta
    def delete_event_by_time_OLD(self, start_time, summary_filter: str = None):
        """
        Briše Google Calendar event ako postoji u zadanom terminu i (opcionalno) ima zadani naziv (summary_filter).
        """
        try:
            # Izračun kraja termina
            duration_hours = app_settings.TERMIN_DURATION + app_settings.CLEAN_DURATION
            end_time = start_time + timedelta(hours=duration_hours)

            # Ispravan ISO format sa vremenskom zonom
            time_min = start_time.isoformat()
            time_max = end_time.isoformat()

            # Dohvati sve evente u tom vremenskom rasponu
            events_result = self.service.events().list(
                calendarId=self.calendar_id,
                timeMin=time_min,
                timeMax=time_max,
                singleEvents=True,
                orderBy='startTime'
            ).execute()

            events = events_result.get('items', [])
            if not events:
                print("ℹ️ Nema eventa u tom terminu za brisanje.")
                return False

            deleted = False
            for event in events:
                event_id = event['id']
                event_summary = event.get('summary', '(bez naziva)')

                # Ako postoji filter po nazivu — preskoči evente koji ne odgovaraju
                if summary_filter and event_summary != summary_filter:
                    print(f"⏭️ Preskačem event '{event_summary}' — ne odgovara nazivu '{summary_filter}'.")
                    continue

                # Briši event
                self.service.events().delete(
                    calendarId=self.calendar_id,
                    eventId=event_id
                ).execute()
                print(f"🗑️ Obrisao event: {event_summary} (ID: {event_id})")
                deleted = True

            if not deleted:
                print("ℹ️ Nije pronađen nijedan event koji odgovara nazivu filtera.")
            return deleted

        except Exception as e:
            print(f"⚠️ Greška pri brisanju eventa: {e}")
            return False


    #******************************************
    # AVAILABILITY - OLD
    def check_avialable_day_OLD(self, no_day: int = 0, 
                        time_step: float = app_settings.TERMIN_STEP):
        # prvi termin: u 8:00
        starting_time = datetime.now().replace(hour=8, minute=0, second=0, microsecond=0) + timedelta(days=no_day)
        # zadnji termin: u 20:00
        ending_time = datetime.now().replace(hour=20, minute=0, second=0, microsecond=1) + timedelta(days=no_day)
        # korak za rezervaciju termina
        step = timedelta(hours=time_step)

        current_time = starting_time
        free_slots = []

        while current_time < ending_time:
            if self.is_calendar_free(current_time):
                free_slots.append(current_time)
            current_time += step

        return free_slots
    
    # Check free termin
    def is_calendar_free(self, service_id, start_time):
        tz = pytz.timezone(self.timezone)
        # Ako start_time nije aware, lokaliziraj ga
        if start_time.tzinfo is None:
            start_time = tz.localize(start_time)
        # Trajanje termina (termin + čišćenje)
        termin_duration = app_settings.termin_duration_h.get(service_id)
        clean_duration = app_settings.clean_duration_h.get(service_id)
        duration_hours = termin_duration + clean_duration
        #duration_hours = app_settings.TERMIN_DURATION + app_settings.CLEAN_DURATION
        end_time = start_time + timedelta(hours=duration_hours)

        # Google Calendar API zahtijeva RFC3339 format
        time_min = start_time.isoformat()  # npr. "2025-10-15T08:00:00+02:00"
        time_max = end_time.isoformat()    # npr. "2025-10-15T10:30:00+02:00"

        # Dohvati evente u tom rasponu
        events_result = self.service.events().list(
            calendarId=self.calendar_id,
            timeMin=time_min,
            timeMax=time_max,
            singleEvents=True,
            orderBy='startTime'
        ).execute()

        events = events_result.get('items', [])

        # Ako postoji event koji se preklapa → zauzeto
        return len(events) == 0
    
    # Add cleaning time (service depend)
    def add_cleaning_time(self, service_id, start_time):
        from dateutil import parser  # dateutil.parser može parsirati ISO stringove s vremenskom zonom
        # primjer datuma iz Google Cal API = '2025-10-28T14:00:00+02:00'
        # parsiranje u datetime objekt
        dt = parser.isoparse(start_time)

        # dodavanje clean duration
        clean_duration = app_settings.clean_duration_h.get(service_id)
        cleaning_time = dt - timedelta(hours=clean_duration)

        # vraćanje u ISO format s vremenskom zonom
        new_date_iso = cleaning_time.isoformat()

        #print("Original:", start_time)
        #print("Plus 30 minuta:", new_date_iso)
        return new_date_iso


# === PRIMJER KORIŠTENJA ===
# 1. check all events: get_events_in_range()
# 2. add new event with status="Prenoted" (under location). Description is optional: add_event(). Summary: "Rezervacija sauna - Customer #ID"
# 3. take event details and store in DB (event_id, status, customer_id, start_time, end_time, description)
# 4. cancel event (by ID): delete_event()
# 5. update event status -> "Prenoted" -> "Paid"
# 6. update DB with new status
# 7. after 24h cancel event if status="Prenoted"
if __name__ == "__main__":
    SERVICE_ACCOUNT_FILE = 'fitandmore-715a01004911.json'
    CALENDAR_ID = 'primary'
    service_id = 2
    #SCOPES = ['https://www.googleapis.com/auth/calendar']
    """ credentials = service_account.Credentials.from_service_account_file(
            SERVICE_ACCOUNT_FILE, scopes=SCOPES
            )

    service = build('calendar', 'v3', credentials=credentials) """


    gc = GoogleCalendarClient(
        service_account_file=SERVICE_ACCOUNT_FILE,
        calendar_id=CALENDAR_ID
    )
    tz = ZoneInfo(gc.timezone)

    # Add new event
    summary = "Rezervacija saune - User #14"
    description = " Test opisa"
    location = 'Prenoted'
    #sauna_termin = 2 # in h
    #sauna_clean = 0.5 # in h
    #start_time = datetime.utcnow() + timedelta(days=1)
    start_time = datetime(2025, 10, 27, 14, 0, 0, tzinfo=tz)
    #end_time = start_time + timedelta(hours=sauna_termin)
    #prev_time = 0
    #after_time = sauna_clean
    ret = gc.add_event(summary, start_time, description, location)
    print(ret)
    #exit()

    # Dohvati rezervacije - FAST (time in UTC)
    first_day = app_settings.DELAY_DAYS
    days_ahead = app_settings.TOTAL_DAYS
    ret = gc.get_events_in_range(service_id,first_day, days_ahead)
    print(ret)
    no = len(ret['events'])
    print(no)
    print(ret['events'][0])
    start_event = ret['events'][0]['start']['dateTime']
    ret2 = gc.add_cleaning_time(start_event)
    exit()

    # Provjeri termin - SLOW
    #tz = ZoneInfo(gc.timezone)
    """ free_slots = []
    for iday in range(app_settings.DELAY_DAYS,app_settings.TOTAL_DAYS):
        ret = gc.check_avialable_day(iday)
        free_slots.append(ret)
    print(free_slots)
    exit() """


    # Delete event by time
    summary = "Rezervacija saune - User 123"
    start_time = datetime(2025, 10, 16, 14, 0, 0, tzinfo=tz)
    ret = gc.delete_event_by_time(start_time, summary)
    exit()


    # Check share list
    user_account="mandul111@gmail.com"
    ret_share = gc.share_checkj(CALENDAR_ID,user_account)

    # Share calendar
    if(ret_share==False):
        gc.share_calendar(CALENDAR_ID, user_account)
    
    ret = gc.share_delete(CALENDAR_ID,user_account)

    # Dohvati evente u idućih 7 dana
    events = gc.get_events(days_ahead=7)

    if not events:
        print("\nNema događaja u idućih 7 dana.")
    else:
        print("\nDogađaji u idućih 7 dana:")
        for event in events:
            start = event['start'].get('dateTime', event['start'].get('date'))
            print(f"{start} — {event.get('summary', '(bez naslova)')}")
