import logging
import msgspec
from fastapi import APIRouter, Depends, HTTPException
from api.v1.schemas.settings import (
    UserPreferences, 
    LidarrSettings, 
    LidarrConnectionSettings,
    JellyfinConnectionSettings,
    JellyfinVerifyResponse,
    JellyfinUserInfo,
    NavidromeConnectionSettings,
    ListenBrainzConnectionSettings,
    YouTubeConnectionSettings,
    HomeSettings,
    LidarrVerifyResponse,
    LocalFilesConnectionSettings,
    LocalFilesVerifyResponse,
    LidarrMetadataProfilePreferences,
    LidarrMetadataProfileSummary,
    LastFmConnectionSettings,
    LastFmConnectionSettingsResponse,
    LastFmVerifyResponse,
    ScrobbleSettings,
    PrimaryMusicSourceSettings,
    PlexConnectionSettings,
    PlexVerifyResponse,
)
from api.v1.schemas.plex import PlexLibrarySectionInfo
from api.v1.schemas.common import VerifyConnectionResponse
from api.v1.schemas.advanced_settings import AdvancedSettingsFrontend, FrontendCacheTTLs, _is_masked_api_key
from core.dependencies import (
    get_preferences_service,
    get_settings_service,
    get_local_files_service,
)
from core.exceptions import ConfigurationError, ExternalServiceError
from infrastructure.msgspec_fastapi import MsgSpecBody, MsgSpecRoute
from services.local_files_service import LocalFilesService
from services.preferences_service import PreferencesService
from services.settings_service import SettingsService

logger = logging.getLogger(__name__)

router = APIRouter(route_class=MsgSpecRoute, prefix="/settings", tags=["settings"])


@router.get("/preferences", response_model=UserPreferences)
async def get_preferences(
    preferences_service: PreferencesService = Depends(get_preferences_service),
):
    return preferences_service.get_preferences()


@router.put("/preferences", response_model=UserPreferences)
async def update_preferences(
    preferences: UserPreferences = MsgSpecBody(UserPreferences),
    preferences_service: PreferencesService = Depends(get_preferences_service),
    settings_service: SettingsService = Depends(get_settings_service),
):
    try:
        preferences_service.save_preferences(preferences)
        total_cleared = await settings_service.clear_caches_for_preference_change()
        return preferences
    except ConfigurationError as e:
        logger.warning(f"Configuration error updating preferences: {e}")
        raise HTTPException(status_code=400, detail="Couldn't save these settings")


@router.get("/lidarr", response_model=LidarrSettings)
async def get_lidarr_settings(
    preferences_service: PreferencesService = Depends(get_preferences_service),
):
    return preferences_service.get_lidarr_settings()


@router.put("/lidarr", response_model=LidarrSettings)
async def update_lidarr_settings(
    lidarr_settings: LidarrSettings = MsgSpecBody(LidarrSettings),
    preferences_service: PreferencesService = Depends(get_preferences_service),
):
    try:
        preferences_service.save_lidarr_settings(lidarr_settings)
        return lidarr_settings
    except ConfigurationError as e:
        logger.warning(f"Configuration error updating Lidarr settings: {e}")
        raise HTTPException(status_code=400, detail="Lidarr settings are incomplete or invalid")


@router.get("/cache-ttls", response_model=FrontendCacheTTLs)
async def get_frontend_cache_ttls(
    preferences_service: PreferencesService = Depends(get_preferences_service),
):
    backend_settings = preferences_service.get_advanced_settings()
    return FrontendCacheTTLs(
        home=backend_settings.frontend_ttl_home,
        discover=backend_settings.frontend_ttl_discover,
        library=backend_settings.frontend_ttl_library,
        recently_added=backend_settings.frontend_ttl_recently_added,
        discover_queue=backend_settings.frontend_ttl_discover_queue,
        search=backend_settings.frontend_ttl_search,
        local_files_sidebar=backend_settings.frontend_ttl_local_files_sidebar,
        jellyfin_sidebar=backend_settings.frontend_ttl_jellyfin_sidebar,
        plex_sidebar=backend_settings.frontend_ttl_plex_sidebar,
        playlist_sources=backend_settings.frontend_ttl_playlist_sources,
        discover_queue_polling_interval=backend_settings.discover_queue_polling_interval,
        discover_queue_auto_generate=backend_settings.discover_queue_auto_generate,
    )


@router.get("/advanced", response_model=AdvancedSettingsFrontend)
async def get_advanced_settings(
    preferences_service: PreferencesService = Depends(get_preferences_service),
):
    backend_settings = preferences_service.get_advanced_settings()
    return AdvancedSettingsFrontend.from_backend(backend_settings)


@router.put("/advanced", response_model=AdvancedSettingsFrontend)
async def update_advanced_settings(
    settings: AdvancedSettingsFrontend = MsgSpecBody(AdvancedSettingsFrontend),
    preferences_service: PreferencesService = Depends(get_preferences_service),
    settings_service: SettingsService = Depends(get_settings_service),
):
    try:
        backend_settings = settings.to_backend()
        if _is_masked_api_key(backend_settings.audiodb_api_key):
            current = preferences_service.get_advanced_settings()
            backend_settings = msgspec.structs.replace(
                backend_settings, audiodb_api_key=current.audiodb_api_key
            )
        preferences_service.save_advanced_settings(backend_settings)
        await settings_service.on_coverart_settings_changed()
        saved = preferences_service.get_advanced_settings()
        return AdvancedSettingsFrontend.from_backend(saved)
    except ConfigurationError as e:
        logger.warning(f"Configuration error updating advanced settings: {e}")
        raise HTTPException(status_code=400, detail="Couldn't save these settings")
    except ValueError as e:
        logger.warning(f"Validation error updating advanced settings: {e}")
        raise HTTPException(status_code=400, detail="That settings value isn't valid")


@router.get("/lidarr/connection", response_model=LidarrConnectionSettings)
async def get_lidarr_connection(
    preferences_service: PreferencesService = Depends(get_preferences_service),
):
    return preferences_service.get_lidarr_connection()


@router.put("/lidarr/connection", response_model=LidarrConnectionSettings)
async def update_lidarr_connection(
    settings: LidarrConnectionSettings = MsgSpecBody(LidarrConnectionSettings),
    preferences_service: PreferencesService = Depends(get_preferences_service),
    settings_service: SettingsService = Depends(get_settings_service),
):
    try:
        from repositories.lidarr.base import reset_lidarr_circuit_breaker
        
        preferences_service.save_lidarr_connection(settings)
        reset_lidarr_circuit_breaker()
        await settings_service.on_lidarr_settings_changed()
        return settings
    except ConfigurationError as e:
        logger.warning(f"Configuration error updating Lidarr connection: {e}")
        raise HTTPException(status_code=400, detail="Lidarr connection settings are incomplete or invalid")


@router.post("/lidarr/verify", response_model=LidarrVerifyResponse)
async def verify_lidarr_connection(
    settings: LidarrConnectionSettings = MsgSpecBody(LidarrConnectionSettings),
    settings_service: SettingsService = Depends(get_settings_service),
):
    return await settings_service.verify_lidarr(settings)


@router.get(
    "/lidarr/metadata-profiles",
    response_model=list[LidarrMetadataProfileSummary],
)
async def list_lidarr_metadata_profiles(
    settings_service: SettingsService = Depends(get_settings_service),
):
    try:
        return await settings_service.list_lidarr_metadata_profiles()
    except ExternalServiceError as e:
        logger.warning(f"Lidarr metadata profiles list failed: {e}")
        raise HTTPException(status_code=502, detail="Couldn't load Lidarr metadata profiles")


@router.get(
    "/lidarr/metadata-profile/preferences",
    response_model=LidarrMetadataProfilePreferences,
)
async def get_lidarr_metadata_profile_preferences(
    profile_id: int | None = None,
    settings_service: SettingsService = Depends(get_settings_service),
):
    try:
        return await settings_service.get_lidarr_metadata_profile_preferences(
            profile_id=profile_id
        )
    except ExternalServiceError as e:
        logger.warning(f"Lidarr metadata profile fetch failed: {e}")
        raise HTTPException(status_code=502, detail="Couldn't load the Lidarr metadata profile")


@router.put(
    "/lidarr/metadata-profile/preferences",
    response_model=LidarrMetadataProfilePreferences,
)
async def update_lidarr_metadata_profile_preferences(
    preferences: UserPreferences = MsgSpecBody(UserPreferences),
    profile_id: int | None = None,
    settings_service: SettingsService = Depends(get_settings_service),
):
    try:
        return await settings_service.update_lidarr_metadata_profile(
            preferences, profile_id=profile_id
        )
    except ExternalServiceError as e:
        logger.warning(f"Lidarr metadata profile update failed: {e}")
        raise HTTPException(status_code=502, detail="Couldn't update the Lidarr metadata profile")


@router.get("/jellyfin", response_model=JellyfinConnectionSettings)
async def get_jellyfin_settings(
    preferences_service: PreferencesService = Depends(get_preferences_service),
):
    return preferences_service.get_jellyfin_connection()


@router.put("/jellyfin", response_model=JellyfinConnectionSettings)
async def update_jellyfin_settings(
    settings: JellyfinConnectionSettings = MsgSpecBody(JellyfinConnectionSettings),
    preferences_service: PreferencesService = Depends(get_preferences_service),
    settings_service: SettingsService = Depends(get_settings_service),
):
    try:
        preferences_service.save_jellyfin_connection(settings)
        await settings_service.on_jellyfin_settings_changed()
        return settings
    except ConfigurationError as e:
        logger.warning(f"Configuration error updating Jellyfin settings: {e}")
        raise HTTPException(status_code=400, detail="Jellyfin settings are incomplete or invalid")


@router.post("/jellyfin/verify", response_model=JellyfinVerifyResponse)
async def verify_jellyfin_connection(
    settings: JellyfinConnectionSettings = MsgSpecBody(JellyfinConnectionSettings),
    settings_service: SettingsService = Depends(get_settings_service),
):
    result = await settings_service.verify_jellyfin(settings)
    users = [JellyfinUserInfo(id=user.id, name=user.name) for user in (result.users or [])] if result.success else []
    return JellyfinVerifyResponse(success=result.success, message=result.message, users=users)


@router.get("/navidrome", response_model=NavidromeConnectionSettings)
async def get_navidrome_settings(
    preferences_service: PreferencesService = Depends(get_preferences_service),
):
    return preferences_service.get_navidrome_connection()


@router.put("/navidrome", response_model=NavidromeConnectionSettings)
async def update_navidrome_settings(
    settings: NavidromeConnectionSettings = MsgSpecBody(NavidromeConnectionSettings),
    preferences_service: PreferencesService = Depends(get_preferences_service),
    settings_service: SettingsService = Depends(get_settings_service),
):
    try:
        preferences_service.save_navidrome_connection(settings)
        await settings_service.on_navidrome_settings_changed(enabled=settings.enabled)
        return preferences_service.get_navidrome_connection()
    except ConfigurationError as e:
        logger.warning("Configuration error updating Navidrome settings: %s", e)
        raise HTTPException(status_code=400, detail="Navidrome settings are incomplete or invalid")


@router.post("/navidrome/verify", response_model=VerifyConnectionResponse)
async def verify_navidrome_connection(
    settings: NavidromeConnectionSettings = MsgSpecBody(NavidromeConnectionSettings),
    settings_service: SettingsService = Depends(get_settings_service),
):
    result = await settings_service.verify_navidrome(settings)
    return VerifyConnectionResponse(valid=result.valid, message=result.message)


@router.get("/plex", response_model=PlexConnectionSettings)
async def get_plex_settings(
    preferences_service: PreferencesService = Depends(get_preferences_service),
):
    return preferences_service.get_plex_connection()


@router.put("/plex", response_model=PlexConnectionSettings)
async def update_plex_settings(
    settings: PlexConnectionSettings = MsgSpecBody(PlexConnectionSettings),
    preferences_service: PreferencesService = Depends(get_preferences_service),
    settings_service: SettingsService = Depends(get_settings_service),
):
    try:
        preferences_service.save_plex_connection(settings)
        await settings_service.on_plex_settings_changed(enabled=settings.enabled)
        logger.info("Updated Plex connection settings")
        return preferences_service.get_plex_connection()
    except ConfigurationError as e:
        logger.warning("Configuration error updating Plex settings: %s", e)
        raise HTTPException(status_code=400, detail="Plex settings are incomplete or invalid")


@router.post("/plex/verify", response_model=PlexVerifyResponse)
async def verify_plex_connection(
    settings: PlexConnectionSettings = MsgSpecBody(PlexConnectionSettings),
    settings_service: SettingsService = Depends(get_settings_service),
):
    result = await settings_service.verify_plex(settings)
    libs = [PlexLibrarySectionInfo(key=k, title=t) for k, t in result.libraries]
    return PlexVerifyResponse(valid=result.valid, message=result.message, libraries=libs)


@router.get("/plex/libraries", response_model=list[PlexLibrarySectionInfo])
async def get_plex_libraries(
    settings_service: SettingsService = Depends(get_settings_service),
):
    try:
        libs = await settings_service.get_plex_libraries()
        return [PlexLibrarySectionInfo(key=k, title=t) for k, t in libs]
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))
    except Exception as e:
        logger.exception("Failed to fetch Plex libraries: %s", e)
        raise HTTPException(status_code=502, detail="Could not fetch libraries from Plex")


@router.get("/listenbrainz", response_model=ListenBrainzConnectionSettings)
async def get_listenbrainz_settings(
    preferences_service: PreferencesService = Depends(get_preferences_service),
):
    return preferences_service.get_listenbrainz_connection()


@router.put("/listenbrainz", response_model=ListenBrainzConnectionSettings)
async def update_listenbrainz_settings(
    settings: ListenBrainzConnectionSettings = MsgSpecBody(ListenBrainzConnectionSettings),
    preferences_service: PreferencesService = Depends(get_preferences_service),
    settings_service: SettingsService = Depends(get_settings_service),
):
    try:
        preferences_service.save_listenbrainz_connection(settings)
        await settings_service.on_listenbrainz_settings_changed()
        return settings
    except ConfigurationError as e:
        logger.warning(f"Configuration error updating ListenBrainz settings: {e}")
        raise HTTPException(status_code=400, detail="ListenBrainz settings are incomplete or invalid")


@router.post("/listenbrainz/verify", response_model=VerifyConnectionResponse)
async def verify_listenbrainz_connection(
    settings: ListenBrainzConnectionSettings = MsgSpecBody(ListenBrainzConnectionSettings),
    settings_service: SettingsService = Depends(get_settings_service),
):
    result = await settings_service.verify_listenbrainz(settings)
    return VerifyConnectionResponse(valid=result.valid, message=result.message)


@router.get("/youtube", response_model=YouTubeConnectionSettings)
async def get_youtube_settings(
    preferences_service: PreferencesService = Depends(get_preferences_service),
):
    return preferences_service.get_youtube_connection()


@router.put("/youtube", response_model=YouTubeConnectionSettings)
async def update_youtube_settings(
    settings: YouTubeConnectionSettings = MsgSpecBody(YouTubeConnectionSettings),
    preferences_service: PreferencesService = Depends(get_preferences_service),
    settings_service: SettingsService = Depends(get_settings_service),
):
    try:
        preferences_service.save_youtube_connection(settings)
        await settings_service.on_youtube_settings_changed()
        return settings
    except ConfigurationError as e:
        logger.warning(f"Configuration error updating YouTube settings: {e}")
        raise HTTPException(status_code=400, detail="YouTube settings are incomplete or invalid")


@router.post("/youtube/verify", response_model=VerifyConnectionResponse)
async def verify_youtube_connection(
    settings: YouTubeConnectionSettings = MsgSpecBody(YouTubeConnectionSettings),
    settings_service: SettingsService = Depends(get_settings_service),
):
    result = await settings_service.verify_youtube(settings)
    return VerifyConnectionResponse(valid=result.valid, message=result.message)


@router.get("/home", response_model=HomeSettings)
async def get_home_settings(
    preferences_service: PreferencesService = Depends(get_preferences_service),
):
    return preferences_service.get_home_settings()


@router.put("/home", response_model=HomeSettings)
async def update_home_settings(
    settings: HomeSettings = MsgSpecBody(HomeSettings),
    preferences_service: PreferencesService = Depends(get_preferences_service),
    settings_service: SettingsService = Depends(get_settings_service),
):
    try:
        preferences_service.save_home_settings(settings)
        await settings_service.clear_home_cache()
        return settings
    except ConfigurationError as e:
        logger.warning(f"Configuration error updating home settings: {e}")
        raise HTTPException(status_code=400, detail="Home settings are incomplete or invalid")


@router.get("/local-files", response_model=LocalFilesConnectionSettings)
async def get_local_files_settings(
    preferences_service: PreferencesService = Depends(get_preferences_service),
):
    return preferences_service.get_local_files_connection()


@router.put("/local-files", response_model=LocalFilesConnectionSettings)
async def update_local_files_settings(
    settings: LocalFilesConnectionSettings = MsgSpecBody(LocalFilesConnectionSettings),
    preferences_service: PreferencesService = Depends(get_preferences_service),
    settings_service: SettingsService = Depends(get_settings_service),
):
    try:
        preferences_service.save_local_files_connection(settings)
        await settings_service.on_local_files_settings_changed()
        return settings
    except ConfigurationError as e:
        logger.warning("Configuration error updating local files settings: %s", e)
        raise HTTPException(status_code=400, detail="Local files settings are incomplete or invalid")


@router.post("/local-files/verify", response_model=LocalFilesVerifyResponse)
async def verify_local_files_connection(
    settings: LocalFilesConnectionSettings = MsgSpecBody(LocalFilesConnectionSettings),
    local_service: LocalFilesService = Depends(get_local_files_service),
) -> LocalFilesVerifyResponse:
    return await local_service.verify_path(settings.music_path)


@router.get("/lastfm", response_model=LastFmConnectionSettingsResponse)
async def get_lastfm_settings(
    preferences_service: PreferencesService = Depends(get_preferences_service),
):
    settings = preferences_service.get_lastfm_connection()
    return LastFmConnectionSettingsResponse.from_settings(settings)


@router.put("/lastfm", response_model=LastFmConnectionSettingsResponse)
async def update_lastfm_settings(
    settings: LastFmConnectionSettings = MsgSpecBody(LastFmConnectionSettings),
    preferences_service: PreferencesService = Depends(get_preferences_service),
    settings_service: SettingsService = Depends(get_settings_service),
):
    try:
        preferences_service.save_lastfm_connection(settings)
        await settings_service.on_lastfm_settings_changed()
        saved = preferences_service.get_lastfm_connection()
        return LastFmConnectionSettingsResponse.from_settings(saved)
    except ConfigurationError as e:
        logger.warning("Configuration error updating Last.fm settings: %s", e)
        raise HTTPException(status_code=400, detail="Last.fm settings are incomplete or invalid")


@router.post("/lastfm/verify", response_model=LastFmVerifyResponse)
async def verify_lastfm_connection(
    settings: LastFmConnectionSettings = MsgSpecBody(LastFmConnectionSettings),
    settings_service: SettingsService = Depends(get_settings_service),
):
    result = await settings_service.verify_lastfm(settings)
    return LastFmVerifyResponse(valid=result.valid, message=result.message)


@router.get("/scrobble", response_model=ScrobbleSettings)
async def get_scrobble_settings(
    preferences_service: PreferencesService = Depends(get_preferences_service),
):
    return preferences_service.get_scrobble_settings()


@router.put("/scrobble", response_model=ScrobbleSettings)
async def update_scrobble_settings(
    settings: ScrobbleSettings = MsgSpecBody(ScrobbleSettings),
    preferences_service: PreferencesService = Depends(get_preferences_service),
):
    try:
        preferences_service.save_scrobble_settings(settings)
        return preferences_service.get_scrobble_settings()
    except ConfigurationError as e:
        logger.warning("Configuration error updating scrobble settings: %s", e)
        raise HTTPException(status_code=400, detail="Scrobbling settings are incomplete or invalid")


@router.get("/primary-source", response_model=PrimaryMusicSourceSettings)
async def get_primary_music_source(
    preferences_service: PreferencesService = Depends(get_preferences_service),
):
    return preferences_service.get_primary_music_source()


@router.put("/primary-source", response_model=PrimaryMusicSourceSettings)
async def update_primary_music_source(
    settings: PrimaryMusicSourceSettings = MsgSpecBody(PrimaryMusicSourceSettings),
    preferences_service: PreferencesService = Depends(get_preferences_service),
    settings_service: SettingsService = Depends(get_settings_service),
):
    try:
        preferences_service.save_primary_music_source(settings)
        await settings_service.clear_home_cache()
        await settings_service.clear_source_resolution_cache()
        return preferences_service.get_primary_music_source()
    except ConfigurationError as e:
        logger.warning("Configuration error updating primary music source: %s", e)
        raise HTTPException(status_code=400, detail="Invalid primary music source")
