import { listenBrainzSettings, lastFMStorage } from './storage.js';

export class ListenBrainzScrobbler {
    constructor() {
        this.DEFAULT_API_URL = 'https://api.listenbrainz.org';
        this.currentTrack = null;
        this.scrobbleTimer = null;
        this.scrobbleThreshold = 0;
        this.hasScrobbled = false;
        this.isScrobbling = false;
        this.lovingTracks = new Set();
    }

    getApiUrl() {
        const customUrl = listenBrainzSettings.getCustomUrl();
        const base = customUrl || this.DEFAULT_API_URL;
        return base.replace(/\/1\/?$/, '');
    }

    isEnabled() {
        return listenBrainzSettings.isEnabled() && !!listenBrainzSettings.getToken();
    }

    getToken() {
        return listenBrainzSettings.getToken();
    }

    _getMetadata(track) {
        if (!track) return null;

        let artistName = 'Unknown Artist';

        if (track.artist?.name) {
            artistName = track.artist.name;
        } else if (typeof track.artist === 'string') {
            artistName = track.artist;
        } else if (track.artists && track.artists.length > 0) {
            const first = track.artists[0];
            artistName = typeof first === 'string' ? first : first.name || 'Unknown Artist';
        }

        if (typeof artistName === 'string') {
            artistName = artistName
                .split(/\s*[&]\s*|\s+feat\.?\s*|\s+ft\.?\s*|\s+featuring\s+|\s+with\s+|\s+x\s+/i)[0]
                .trim();
        }

        const payload = {
            artist_name: artistName,
            track_name: track.cleanTitle || track.title,
            additional_info: {
                submission_client: 'Monochrome',
                submission_client_version: '1.0.0',
            },
        };

        if (track.album?.title) {
            payload.release_name = track.album.title;
        }

        if (track.duration) {
            payload.additional_info.duration = Math.floor(track.duration);
        }

        if (track.trackNumber) {
            payload.additional_info.track_number = track.trackNumber;
        }

        if (track.isLocal) {
            payload.additional_info.is_local = true;
        }

        if (track.mbids) {
            if (track.mbids.recording_mbid) {
                payload.additional_info.recording_mbid = track.mbids.recording_mbid;
            }
            if (track.mbids.release_mbid) {
                payload.additional_info.release_mbid = track.mbids.release_mbid;
            }
            if (track.mbids.artist_mbids) {
                payload.additional_info.artist_mbids = track.mbids.artist_mbids;
            }
        }

        return payload;
    }

    async _lookupMbids(track) {
        if (track.mbids?.recording_mbid) return track.mbids;
        let with_album = true;
        const metadata = this._getMetadata(track);
        if (!metadata || !metadata.artist_name || !metadata.track_name) return null;

        try {
            const apiUrl = this.getApiUrl();
            const params = new URLSearchParams({
                recording_name: metadata.track_name,
                artist_name: metadata.artist_name,
            });

            if (track.album?.title) {
                params.append('release_name', track.album.title);
            }

            let response = await fetch(`${apiUrl}/1/metadata/lookup/?${params}`, {
                method: 'GET',
                headers: {
                    Authorization: `Token ${this.getToken()}`,
                },
            });

            if (!response.ok) {
                console.warn(`[ListenBrainz] MBID lookup failed, trying without album`);
                with_album = false;
                const params = new URLSearchParams({
                    recording_name: metadata.track_name,
                    artist_name: metadata.artist_name,
                });
                response = await fetch(`${apiUrl}/1/metadata/lookup/?${params}`, {
                    method: 'GET',
                    headers: {
                        Authorization: `Token ${this.getToken()}`,
                    },
                });
                if (!response.ok) {
                    console.warn(`[ListenBrainz] MBID lookup failed: ${response.status}`);
                    return null;
                }
            }

            const data = await response.json();
            if (data?.recording_mbid) {
                track.mbids = {
                    recording_mbid: data.recording_mbid,
                    artist_mbids: data.artist_mbids,
                };
                if (with_album) {
                    track.mbids.release_mbid = data.release_mbid;
                }
                console.log(`[ListenBrainz] Found MBID: ${data.recording_mbid}`);
                return track.mbids;
            }
            console.warn('[ListenBrainz] No recording_mbid found in lookup response');
        } catch (error) {
            console.error('[ListenBrainz] MBID lookup error:', error);
        }
        return null;
    }

    async submitListen(listenType, track, timestamp = null) {
        if (!this.isEnabled()) return;
        await this._lookupMbids(track);
        const metadata = this._getMetadata(track);
        if (!metadata) return;

        const payload = [
            {
                track_metadata: metadata,
            },
        ];

        if (timestamp) {
            payload[0].listened_at = timestamp;
        }

        const body = {
            listen_type: listenType,
            payload: payload,
        };

        try {
            const apiUrl = this.getApiUrl();
            const response = await fetch(`${apiUrl}/1/submit-listens`, {
                method: 'POST',
                headers: {
                    Authorization: `Token ${this.getToken()}`,
                    'Content-Type': 'application/json',
                },
                body: JSON.stringify(body),
            });

            if (!response.ok) {
                const text = await response.text();
                throw new Error(`ListenBrainz API Error ${response.status}: ${text}`);
            }

            console.log(
                `[ListenBrainz] Submitted ${listenType}: ${metadata.track_name} ${metadata.artist_name} ${metadata.release_name}`
            );
        } catch (error) {
            console.error('[ListenBrainz] Submission failed:', error);
        }
    }

    async updateNowPlaying(track) {
        if (!this.isEnabled()) return;

        this.currentTrack = track;
        if (!this.isScrobbling) {
            this.hasScrobbled = false;
        }
        this.clearScrobbleTimer();
        await this.submitListen('playing_now', track);

        const scrobblePercentage = lastFMStorage.getScrobblePercentage() / 100;
        this.scrobbleThreshold = Math.min(track.duration * scrobblePercentage, 240);
        this.scheduleScrobble(this.scrobbleThreshold * 1000);
    }

    scheduleScrobble(delay) {
        this.clearScrobbleTimer();
        this.scrobbleTimer = setTimeout(async () => {
            await this.scrobbleCurrentTrack();
        }, delay);
    }

    clearScrobbleTimer() {
        if (this.scrobbleTimer) {
            clearTimeout(this.scrobbleTimer);
            this.scrobbleTimer = null;
        }
    }

    async scrobbleCurrentTrack() {
        if (!this.isEnabled() || !this.currentTrack || this.hasScrobbled) return;

        this.isScrobbling = true;

        try {
            const timestamp = Math.floor(Date.now() / 1000);
            await this.submitListen('single', this.currentTrack, timestamp);
            this.hasScrobbled = true;
        } finally {
            this.isScrobbling = false;
        }
    }

    async onTrackChange(track) {
        await this.updateNowPlaying(track);
    }

    onPlaybackStop() {
        this.clearScrobbleTimer();
    }

    disconnect() {
        this.clearScrobbleTimer();
        this.currentTrack = null;
    }

    async loveTrack(track) {
        if (!track.artist?.name || !track.title) return;
        const trackKey = `${track.artist.name}-${track.title}`;
        if (!this.isEnabled() || this.lovingTracks.has(trackKey)) return;
        this.lovingTracks.add(trackKey);

        try {
            const apiUrl = this.getApiUrl();
            const mbids = await this._lookupMbids(track);
            const mbid = mbids?.recording_mbid;

            if (mbid) {
                const response = await fetch(`${apiUrl}/1/feedback/recording-feedback`, {
                    method: 'POST',
                    headers: {
                        Authorization: `Token ${this.getToken()}`,
                        'Content-Type': 'application/json',
                    },
                    body: JSON.stringify({
                        recording_mbid: mbid,
                        score: 1,
                    }),
                });

                if (!response.ok) {
                    const text = await response.text();
                    throw new Error(`ListenBrainz Feedback Error ${response.status}: ${text}`);
                }

                console.log('[ListenBrainz] Loved track:', track.title);
            } else {
                console.warn('[ListenBrainz] Could not find recording MBID for love feedback');
            }
        } catch (error) {
            console.error('[ListenBrainz] Failed to love track:', error);
        } finally {
            this.lovingTracks.delete(trackKey);
        }
    }
}
