package qbittorrent

import (
	"context"
	"errors"
	"fmt"
	"log/slog"
	"os"
	"path"
	"slices"

	"git.kmsign.ru/royalcat/tstor/plugins/qbittorrent/pkg/qbittorrent"
	"git.kmsign.ru/royalcat/tstor/server/pkg/rlog"
)

func (d *Daemon) Cleanup(ctx context.Context, run bool) ([]string, error) {
	d.log.Info(ctx, "cleanup started")

	torrentInfos, err := d.client.qb.Torrent().GetTorrents(ctx, &qbittorrent.TorrentOption{})
	if err != nil {
		d.log.Error(ctx, "failed to get torrents", rlog.Error(err))
		return nil, fmt.Errorf("failed to get torrents: %w", err)
	}
	daemonsHashes := []string{}
	for _, info := range torrentInfos {
		daemonsHashes = append(daemonsHashes, info.Hash)
	}

	dataDirs, err := os.ReadDir(d.dataDir)
	if err != nil {
		d.log.Error(ctx, "failed to read data directory", slog.String("path", d.dataDir), rlog.Error(err))
		return nil, fmt.Errorf("failed to read data directory: %w", err)
	}
	dataHashes := []string{}
	for _, entry := range dataDirs {
		dataHashes = append(dataHashes, entry.Name())
	}

	hashToDelete := make([]string, 0, 5)

	for _, v := range dataHashes {
		if !slices.Contains(daemonsHashes, v) {
			hashToDelete = append(hashToDelete, v)
		}
	}

	d.log.Info(ctx, "marked torrents to delete",
		slog.Int("count", len(hashToDelete)),
		slog.Any("infohashes", hashToDelete),
	)

	if !run {
		d.log.Info(ctx, "dry run, skipping deletion")
		return hashToDelete, nil
	}

	for _, hash := range hashToDelete {
		d.log.Info(ctx, "deleting stale torrent data", slog.String("infohash", hash))
		err := os.RemoveAll(path.Join(d.dataDir, hash))
		if err != nil {
			d.log.Error(ctx, "failed to delete torrent data", slog.String("infohash", hash), rlog.Error(err))
			return nil, fmt.Errorf("failed to delete torrent data: %w", err)
		}
	}

	return hashToDelete, nil
}

func (d *Daemon) CleanupUnregistred(ctx context.Context, run bool) ([]string, error) {
	d.log.Info(ctx, "cleanup started")

	torrentInfos, err := d.client.qb.Torrent().GetTorrents(ctx, &qbittorrent.TorrentOption{})
	if err != nil {
		d.log.Error(ctx, "failed to get torrents", rlog.Error(err))
		return nil, fmt.Errorf("failed to get torrents: %w", err)
	}

	torrentToDelete := make([]string, 0, 5)

	for _, info := range torrentInfos {
		if d.registeredTorrents.Contains(info.Hash) {
			continue
		}

		d.log.Info(ctx, "torrent not found in registry", slog.String("infohash", info.Hash))
		torrentToDelete = append(torrentToDelete, info.Hash)
	}

	d.log.Info(ctx, "marked torrents to delete",
		slog.Int("count", len(torrentToDelete)),
		slog.Any("infohashes", torrentToDelete),
	)

	if !run {
		d.log.Info(ctx, "dry run, skipping deletion")
		return torrentToDelete, nil
	}

	err = d.client.qb.Torrent().DeleteTorrents(ctx, torrentToDelete, true)
	if err != nil {
		d.log.Error(ctx, "failed to delete torrents", slog.Any("infohashes", torrentToDelete), rlog.Error(err))
		return nil, fmt.Errorf("failed to delete torrents: %w", err)
	}
	d.log.Info(ctx, "torrents deleted from qbittorrent", slog.Int("count", len(torrentToDelete)))

	for _, hash := range torrentToDelete {
		torrentPath := path.Join(d.dataDir, hash)
		_, err := os.Stat(torrentPath)
		if errors.Is(err, os.ErrNotExist) {
			continue
		}
		if err != nil {
			d.log.Error(ctx, "failed to get torrent path", slog.String("path", torrentPath), rlog.Error(err))
			continue
		}
		d.log.Warn(ctx, "leftover data for torrent detected, cleaning up", slog.String("infohash", hash), slog.String("path", torrentPath))
	}

	return torrentToDelete, nil
}