package main

import (
	"context"
	"encoding/base64"
	"fmt"
	"github.com/pquerna/otp/totp"
	"log/slog"
	"strings"
	"time"

	twitter "github.com/g8rswimmer/go-twitter/v2"
	twitterscraper "github.com/imperatrona/twitter-scraper"
	"golang.org/x/time/rate"
)

func generateTOTP(secret string) (string, error) {
	return totp.GenerateCode(secret, time.Now())
}

func GetLoginAccount() ([]ScraperWithTimer, error) {

	accounts, err := GetAvailableAccounts()

	if err != nil {
		return nil, err
	}

	fmt.Println("len(accounts)", len(accounts))

	res := make([]ScraperWithTimer, 0, len(accounts))

	for _, v := range accounts {

		needLogin := false

		scraper := twitterscraper.New()

		fmt.Println("user cookies ", v.Username, v.Email)

		if v.Cookies != nil {
			//var cookies []*http.Cookie

			// if err := json.Unmarshal(v.Cookies, cookies); err != nil {
			// 	slog.Error("cookies json.Unmarshal(", "err", err.Error())
			// 	needLogin = true
			// }
			scraper.SetCookies(v.Cookies)
			if !scraper.IsLoggedIn() {
				needLogin = true
			} else {
				res = append(res, ScraperWithTimer{
					Scraper:     scraper,
					AccountInfo: v,
				})
				continue
			}
		} else {
			needLogin = true
		}

		fmt.Println("needLogin", needLogin)

		if needLogin {
			if v.F2A != "" {
				code, _ := generateTOTP(v.F2A)
				if err := scraper.AutoLogin(v.Username, v.Password, v.Email, code); err != nil {
					SetNotAvailable(v.Username, err.Error())
					slog.Error("scraper.Login", "err", err.Error())
					continue
				}
			} else {
				if err := scraper.AutoLogin(v.Username, v.Password, v.Email); err != nil {
					SetNotAvailable(v.Username, err.Error())
					slog.Error("scraper.Login", "err", err.Error())
					continue
				}
			}
			cookies := scraper.GetCookies()

			if err := UpdateCookies(v.Username, cookies); err != nil {
				slog.Error("cookies UpdateCookies", "err", err.Error())
				continue
			}

			res = append(res, ScraperWithTimer{
				Scraper:     scraper,
				AccountInfo: v,
			})
		}
	}

	return res, nil
}

type ScraperWithTimer struct {
	*twitterscraper.Scraper
	AccountInfo Account
	Timer       *time.Timer
}

var accChan chan ScraperWithTimer = make(chan ScraperWithTimer, 20)

// func init() {

// }
func NewFollowerOb() *FollowerRateLimit {

	return &FollowerRateLimit{
		RateLimit: rate.NewLimiter(rate.Every(15*time.Minute), 40),
	}
}

type FollowerRateLimit struct {
	RateLimit *rate.Limiter
	Scraper   *twitterscraper.Scraper
}

func (f *FollowerRateLimit) TryProfileFollowerCount(username string) (int, error) {

	tryCount := 0
	for {
		if tryCount > 10 {
			return 0, fmt.Errorf("can not get the %v follower count", username)
		}

		fc, err := f.ProfileFollowerCount(username)

		if err != nil {

			f.Scraper.GetGuestToken()
			// twitterscraper.GetGuestToken()
			// c.Scraper = twitterscraper.New()
			slog.Error("ProfileFollowerCount", "err", err.Error())

			tryCount++
			time.Sleep(time.Second * time.Duration(tryCount))

			continue
		}

		return fc, nil
	}

}

func (f *FollowerRateLimit) ProfileFollowerCount(username string) (int, error) {

	if f.Scraper == nil {
		f.Scraper = twitterscraper.New()
	}

	pro, err := f.Scraper.GetProfile(username)

	if err != nil {
		return 0, err
	}

	return pro.FollowersCount, nil

}

//= rate.NewLimiter(rate.Every(15*time.Minute), 40)

func (f *FollowerRateLimit) Follower(userName string, cursor string) ([]*twitter.UserObj, string, *twitter.RateLimit, error) {

	slog.Info("Follower request", "userName", userName, "cursor", cursor)

	ctx := context.Background()

	if err := f.RateLimit.Wait(ctx); err != nil { // This is a blocking call.
		return nil, "", nil, err
	}
	var (
		history = make(map[string]bool)
		users   []*twitterscraper.Profile
		res     []*twitter.UserObj
		next    string
		err     error
		success bool = false
		try          = 0
	)
	for !success && try < 10 {
		select {
		case account := <-accChan:
			accChan <- account
			if _, exist := history[account.AccountInfo.Username]; exist {
				// loop all account, exit.
				try = 100
				break
			}
			history[account.AccountInfo.Username] = true
			if users, next, err = account.FetchFollowers(userName, 1000, cursor); err != nil {
				slog.Error("FetchFollowers", "failed", err.Error())
				continue
			}

			success = true
			res = make([]*twitter.UserObj, 0, len(users))

			for _, v := range users {
				sDec, _ := base64.StdEncoding.DecodeString(v.UserID)
				userId, _ := strings.CutPrefix(string(sDec), "User:")

				res = append(res, &twitter.UserObj{
					ID:       userId,
					Name:     v.Name,
					UserName: v.Username,
				})
			}
		}
	}
	return res, next, nil, err
}
