import click,requests,json
from flask.cli import with_appcontext
from flask import current_app
from datetime import datetime, timedelta, date
from sqlalchemy import and_
from extensions import db
from models import Attendance, Employee, PublicHoliday, LeaveRequest, DesktimeLog, WeekendSettings, SyncLog
from typing import Optional
from sqlalchemy import func
from common_utils import get_setting



DESKTIME_API_KEY = "f9da2cfcf20ff9508002b5a74aa0ff59"
DESKTIME_URL = "https://desktime.com/api/v2/json/employee"

def _get_desktime_api_key():
    return get_setting("desktime.api_key", default=None)


def _is_desktime_enabled():
    return get_setting("desktime.enabled", default=False, cast=bool)


def fetch_and_store_desktime(employee, target_date):
    formatted_date = target_date.strftime('%Y-%m-%d')
    url = f"{DESKTIME_URL}?apiKey={DESKTIME_API_KEY}&id={employee.desktime_id}&date={formatted_date}"

    try:
        response = requests.get(url)
        if response.status_code == 200:
            data = response.json()

            productive_seconds = data.get('productiveTime', 0)
            first_login = data.get('arrived')
            last_logout = data.get('left')

            log = DesktimeLog.query.filter_by(employee_id=employee.id, date=target_date).first()
            if not log:
                log = DesktimeLog(employee_id=employee.id, date=target_date)

            log.first_activity = datetime.strptime(first_login, '%Y-%m-%d %H:%M:%S').time() if first_login else None
            log.last_activity = datetime.strptime(last_logout, '%Y-%m-%d %H:%M:%S').time() if last_logout else None
            log.productive_hours = productive_seconds / 3600 if productive_seconds else 0
            log.raw_data = json.dumps(data)

            db.session.add(log)
            db.session.commit()

            return log.productive_hours
        else:
            print(f"\u26a0\ufe0f Desktime API failed for {employee.desktime_id} on {formatted_date}: Status {response.status_code}")
    except Exception as e:
        print(f"\u274c Error fetching Desktime for {employee.employee_code} on {formatted_date}: {e}")

    return 0

# ... (imports and setup remain same)

@click.command('process-attendance')
@with_appcontext
def process_attendance():
    today = date.today()
    fy_start = today.replace(month=4, day=1) if today.month >= 4 else date(today.year - 1, 4, 1)
    fy_end = fy_start.replace(year=fy_start.year + 1) - timedelta(days=1)

    employees = Employee.query.filter_by(status='active').all()
    weekend_settings = WeekendSettings.query.order_by(WeekendSettings.updated_on.desc()).first()

    def is_saturday_off(day):
        if day.weekday() != 5:
            return False
        week_number = (day.day - 1) // 7 + 1
        attr = f"{['first', 'second', 'third', 'fourth', 'fifth'][week_number - 1]}_saturday"
        return getattr(weekend_settings, attr) == 'off'

    def is_sunday_off(day):
        return day.weekday() == 6 and weekend_settings.sunday == 'off'

    for emp in employees:
        for single_date in (fy_start + timedelta(n) for n in range((fy_end - fy_start).days + 1)):

            if single_date > today:
                continue  # ⛔ Skip future dates

            is_weekend = is_saturday_off(single_date) or is_sunday_off(single_date)
            is_holiday = PublicHoliday.query.filter_by(date=single_date).first() is not None
            is_leave = LeaveRequest.query.filter_by(employee_id=emp.id, status='final_approved')\
                .filter(LeaveRequest.start_date <= single_date, LeaveRequest.end_date >= single_date).first() is not None

            existing = Attendance.query.filter_by(employee_id=emp.id, date=single_date).first()

            # ✅ If Desktime entry already exists, skip (unless you're explicitly reprocessing)
            if existing and existing.source == 'Desktime':
                continue

            hours = 0
            source = 'System'

            if is_holiday:
                remark = 'Holiday'
                source = 'Holiday'
            elif is_leave:
                remark = 'Leave'
                source = 'Leave'
            elif is_weekend:
                remark = 'Holiday'
                source = 'Weekend'
            else:
                if existing and existing.check_in_time and existing.check_out_time:
                    diff = datetime.combine(date.min, existing.check_out_time) - datetime.combine(date.min, existing.check_in_time)
                    hours = round(diff.total_seconds() / 3600, 2)
                    source = 'Punch'
                elif emp.desktime_id:
                    hours = fetch_and_store_desktime(emp, single_date)
                    if hours > 0:
                        source = 'Desktime'

                if hours >= 7:
                    remark = 'Present'
                elif 3.5 <= hours < 7:
                    remark = 'Half Day'
                elif 0 < hours < 3.5:
                    remark = 'LWP'
                else:
                    remark = 'LWP'

            if existing:
                existing.working_hours = hours
                existing.remarks = remark
                existing.source = source
            else:
                db.session.add(Attendance(
                    employee_id=emp.id,
                    date=single_date,
                    check_in_time=None,
                    check_out_time=None,
                    working_hours=hours,
                    remarks=remark,
                    source=source
                ))

    db.session.commit()
    click.echo("✅ Attendance processing complete.")


@click.command('desktime-sync')
@with_appcontext
def desktime_sync():
    # Adjust the import path as needed based on your project structure
    from utils.desktime import sync_all_desktime_data  # replace with your actual sync function

    log = SyncLog(started_at=datetime.now(), status='Started')
    db.session.add(log)
    db.session.commit()

    try:
        result = sync_all_desktime_data()
        log.status = 'Completed'
        log.message = f"Synced: {result.get('synced', 0)}, Errors: {result.get('errors', 0)}"
    except Exception as e:
        log.status = 'Failed'
        log.message = str(e)
    finally:
        log.ended_at = datetime.now()
        db.session.commit()

def run_daily_desktime_sync(target_date: Optional[date] = None) -> SyncLog:
    
    if not _is_desktime_enabled():
        current_app.logger.info("DeskTime sync skipped – integration disabled in settings.")
        # you can also create a SyncLog row with status 'skipped'
        ...

    api_key = _get_desktime_api_key()
    if not api_key:
        current_app.logger.error("DeskTime sync failed – no API key configured.")
    """
    Fetch DeskTime data for all employees that have desktime_id
    for a single day (default = yesterday), and log summary to SyncLog.
    """
    if target_date is None:
        # run for "yesterday" so the day is fully complete
        target_date = date.today() - timedelta(days=1)

    log_entry = SyncLog(
        started_at=datetime.utcnow(),
        status="running",
        message=f"Starting daily DeskTime sync for {target_date.isoformat()}",
    )
    db.session.add(log_entry)
    db.session.commit()

    allowed_statuses = ('active', 'notice period', 'notice_period')

    employees = (
        Employee.query
        .filter(
            Employee.desktime_id.isnot(None),
            func.lower(Employee.status).in_([s.lower() for s in allowed_statuses])
        )
        .all()
    )

    total = len(employees)
    success = 0
    failed = 0
    errors: list[str] = []

    for emp in employees:
        try:
            # clear existing entries for that day to keep data idempotent
            Attendance.query.filter_by(employee_id=emp.id, date=target_date).delete()
            DesktimeLog.query.filter_by(employee_id=emp.id, date=target_date).delete()
            db.session.commit()

            # fetch + store via your existing helper
            fetch_and_store_desktime(emp, target_date)

            success += 1
        except Exception as e:
            db.session.rollback()
            failed += 1
            # keep error text short for storage
            errors.append(
                f"{emp.employee_code} - {emp.full_name}: {str(e)[:200]}"
            )

    log_entry.ended_at = datetime.utcnow()
    if failed == 0 and success > 0:
        status = "success"
    elif success > 0:
        status = "partial"
    else:
        status = "failed"

    payload = {
        "target_date": target_date.isoformat(),
        "total_employees": total,
        "success": success,
        "failed": failed,
        "errors": errors,
    }

    log_entry.status = status
    log_entry.message = json.dumps(payload, ensure_ascii=False)
    db.session.commit()

    return log_entry