"""
OTB-387: API endpoints for Economic Calendar
"""
from datetime import date, timedelta

from fastapi import APIRouter, Query, Request
from loguru import logger
from pydantic import BaseModel

from optrabot import crud, schemas
from optrabot.database import SessionLocal
from optrabot.economiccalendar import (COUNTRY_IDS, IMPORTANCE_IDS,
                                       SOURCE_TIMEZONE, TIMEZONE_IDS,
                                       EconomicCalendarService)

router = APIRouter(prefix='/api')


# ══════════════════════════════════════════════════════════════════════════════
# Constants response (available filters)
# ══════════════════════════════════════════════════════════════════════════════

class CalendarFiltersResponse(BaseModel):
	countries: list[str]
	importances: list[str]
	timezones: list[str]


@router.get('/calendar/filters', response_model=CalendarFiltersResponse)
async def get_calendar_filters() -> CalendarFiltersResponse:
	"""Return available filter options for the economic calendar."""
	return CalendarFiltersResponse(
		countries=sorted(COUNTRY_IDS.keys()),
		importances=sorted(IMPORTANCE_IDS.keys()),
		timezones=sorted(TIMEZONE_IDS.keys()),
	)


# ══════════════════════════════════════════════════════════════════════════════
# Dashboard events
# ══════════════════════════════════════════════════════════════════════════════

@router.get('/calendar/dashboard', response_model=schemas.CalendarEventsResponse)
async def get_dashboard_events(request: Request) -> schemas.CalendarEventsResponse:
	"""
	Get today's economic events filtered by dashboard settings.
	Used by the dashboard widget.
	If no data is cached for today, triggers a fetch from Investing.com.
	"""
	optrabot = request.app.optraBot
	calendar_service: EconomicCalendarService = getattr(optrabot, 'economicCalendar', None)

	today = date.today()
	if calendar_service is None:
		return schemas.CalendarEventsResponse(
			from_date=today, to_date=today, events=[], total=0,
			source_timezone=SOURCE_TIMEZONE,
		)

	# Check if today is cached; if not, fetch from Investing.com
	with SessionLocal() as session:
		cached_dates = crud.get_cached_event_dates(session, today, today)
		if today not in cached_dates:
			logger.info('Dashboard: no cached data for today ({}), fetching...', today)
			# Fetch with ALL importance levels and countries for complete cache
			all_importances = list(IMPORTANCE_IDS.keys())
			all_countries = list(COUNTRY_IDS.keys())
			await calendar_service.fetch_and_store(
				today, today,
				countries=all_countries,
				importances=all_importances,
			)

	with SessionLocal() as session:
		events = calendar_service.get_dashboard_events(session)
		event_list = [schemas.EconomicEventResponse.model_validate(e) for e in events]

	return schemas.CalendarEventsResponse(
		from_date=today,
		to_date=today,
		events=event_list,
		total=len(event_list),
		source_timezone=SOURCE_TIMEZONE,
	)


# ══════════════════════════════════════════════════════════════════════════════
# Events list (for the calendar page)
# ══════════════════════════════════════════════════════════════════════════════

@router.get('/calendar/events', response_model=schemas.CalendarEventsResponse)
async def get_calendar_events(
	request: Request,
	period: str = Query('today', regex='^(today|this_week|next_week)$'),
	countries: list[str] | None = Query(None, alias='countries[]'),
	importances: list[int] | None = Query(None, alias='importances[]'),
) -> schemas.CalendarEventsResponse:
	"""
	Get economic events for the specified period with optional filters.
	Fetches from Investing.com if data is not cached for the requested range.
	"""
	optrabot = request.app.optraBot
	calendar_service: EconomicCalendarService = getattr(optrabot, 'economicCalendar', None)

	today = date.today()
	if period == 'today':
		from_date, to_date = today, today
	elif period == 'this_week':
		# Monday to Friday of current week
		from_date = today - timedelta(days=today.weekday())
		to_date = from_date + timedelta(days=4)
	elif period == 'next_week':
		# Monday to Friday of next week
		next_monday = today + timedelta(days=(7 - today.weekday()))
		from_date = next_monday
		to_date = next_monday + timedelta(days=4)
	else:
		from_date, to_date = today, today

	if calendar_service is None:
		return schemas.CalendarEventsResponse(
			from_date=from_date, to_date=to_date, events=[], total=0,
			source_timezone=SOURCE_TIMEZONE,
		)

	# Check if we need to fetch data for the requested range.
	# Compare cached dates against all weekdays in the range – if any weekday
	# is missing, fetch the entire range from Investing.com.
	with SessionLocal() as session:
		cached_dates = crud.get_cached_event_dates(session, from_date, to_date)
		# Build set of expected weekdays (Mon-Fri) in the range
		expected_dates = set()
		current = from_date
		while current <= to_date:
			if current.weekday() < 5:  # Mon=0 … Fri=4
				expected_dates.add(current)
			current += timedelta(days=1)
		missing_dates = expected_dates - cached_dates
		if missing_dates:
			logger.info(
				'Missing cached data for {} day(s) in period {} ({} to {}): {}',
				len(missing_dates), period, from_date, to_date,
				', '.join(d.isoformat() for d in sorted(missing_dates)),
			)
			# Fetch with ALL importance levels and ALL configured countries
			# so the cache is as complete as possible.  The UI filters are
			# applied later when reading from the DB.
			all_importances = list(IMPORTANCE_IDS.keys())      # low, medium, high
			all_countries   = list(COUNTRY_IDS.keys())          # all 10 countries
			await calendar_service.fetch_and_store(
				from_date, to_date,
				countries=all_countries,
				importances=all_importances,
			)

	# Now read from DB with filters
	with SessionLocal() as session:
		events = calendar_service.get_events(
			session, from_date, to_date,
			countries=countries,
			importances=importances,
		)
		event_list = [schemas.EconomicEventResponse.model_validate(e) for e in events]

	return schemas.CalendarEventsResponse(
		from_date=from_date,
		to_date=to_date,
		events=event_list,
		total=len(event_list),
		source_timezone=SOURCE_TIMEZONE,
	)


# ══════════════════════════════════════════════════════════════════════════════
# Settings
# ══════════════════════════════════════════════════════════════════════════════

@router.get('/calendar/settings', response_model=schemas.CalendarSettingsResponse)
async def get_calendar_settings(request: Request) -> schemas.CalendarSettingsResponse:
	"""Get current calendar display settings."""
	optrabot = request.app.optraBot
	calendar_service: EconomicCalendarService = getattr(optrabot, 'economicCalendar', None)

	if calendar_service is None:
		return schemas.CalendarSettingsResponse(
			dashboard_countries=['united states'],
			dashboard_importance=['high'],
			timezone='GMT +1:00',
		)

	with SessionLocal() as session:
		settings = calendar_service.get_settings(session)

	return schemas.CalendarSettingsResponse(**settings)


@router.post('/calendar/settings', response_model=schemas.CalendarSettingsResponse)
async def update_calendar_settings(
	request: Request,
	body: schemas.CalendarSettingsRequest,
) -> schemas.CalendarSettingsResponse:
	"""Update calendar display settings."""
	optrabot = request.app.optraBot
	calendar_service: EconomicCalendarService = getattr(optrabot, 'economicCalendar', None)

	if calendar_service is None:
		return schemas.CalendarSettingsResponse(
			dashboard_countries=['united states'],
			dashboard_importance=['high'],
			timezone='GMT +1:00',
		)

	with SessionLocal() as session:
		calendar_service.save_settings(
			session,
			dashboard_countries=body.dashboard_countries,
			dashboard_importance=body.dashboard_importance,
			timezone=body.timezone,
		)
		settings = calendar_service.get_settings(session)

	return schemas.CalendarSettingsResponse(**settings)


# ══════════════════════════════════════════════════════════════════════════════
# Manual refresh
# ══════════════════════════════════════════════════════════════════════════════

class RefreshResponse(BaseModel):
	success: bool
	events_count: int
	message: str


@router.post('/calendar/refresh', response_model=RefreshResponse)
async def refresh_calendar(request: Request) -> RefreshResponse:
	"""Manually trigger a refresh of the economic calendar for today."""
	optrabot = request.app.optraBot
	calendar_service: EconomicCalendarService = getattr(optrabot, 'economicCalendar', None)

	if calendar_service is None:
		return RefreshResponse(success=False, events_count=0, message='Calendar service not available')

	today = date.today()
	count = await calendar_service.fetch_and_store(today, today)
	return RefreshResponse(
		success=count > 0,
		events_count=count,
		message=f'{count} events fetched and stored' if count > 0 else 'No events found or fetch failed',
	)
