package provider

import (
	"context"
	"fmt"
	"net/url"
	"strings"

	"apps/backend/internal/module/threads/domain"
)

// FakeProvider implements OAuth without Meta (dev + tests).
// AuthorizeURL points at callbackURL with code=fake&state=...
type FakeProvider struct {
	// CallbackBase is full callback path e.g. http://127.0.0.1:8888/api/v1/threads-accounts/oauth/callback
	CallbackBase string
	// FailExchange forces TH-04
	FailExchange bool
	// FailRefresh forces TH-07
	FailRefresh bool
	// FixedUserID when set, Exchange always returns this Threads user id (reconnect tests)
	FixedUserID string
	// FixedUsername optional with FixedUserID
	FixedUsername string
}

func (f *FakeProvider) Name() string { return "fake" }

func (f *FakeProvider) AuthorizeURL(state, redirectURI string) string {
	base := f.CallbackBase
	if base == "" {
		base = redirectURI
	}
	u, err := url.Parse(base)
	if err != nil {
		return base + "?code=fake&state=" + url.QueryEscape(state)
	}
	q := u.Query()
	q.Set("code", "fake")
	q.Set("state", state)
	u.RawQuery = q.Encode()
	return u.String()
}

func (f *FakeProvider) ExchangeCode(ctx context.Context, code, redirectURI string) (*domain.TokenPair, error) {
	_ = ctx
	_ = redirectURI
	if f.FailExchange || code == "fail" {
		return nil, domain.ErrOAuthExchange
	}
	if code != "fake" && !strings.HasPrefix(code, "fake") {
		// still accept any code in pure fake mode for flexibility
	}
	id := f.FixedUserID
	if id == "" {
		id = fmt.Sprintf("fake_%d", domain.NowNano()%1_000_000)
	}
	user := f.FixedUsername
	if user == "" {
		user = "threads_" + id
	}
	return &domain.TokenPair{
		AccessToken:  "fake-access-" + id + fmt.Sprintf("_%d", domain.NowNano()%10000),
		RefreshToken: "fake-refresh-" + id,
		ExpiresIn:    3600 * 24 * 60, // ~60d
		UserID:       id,
		Username:     user,
		DisplayName:  "Threads " + user,
		AvatarURL:    "",
	}, nil
}

func (f *FakeProvider) Refresh(ctx context.Context, refreshToken string) (*domain.TokenPair, error) {
	_ = ctx
	if f.FailRefresh || refreshToken == "" || strings.Contains(refreshToken, "bad") {
		return nil, domain.ErrRefreshFailed
	}
	id := "refreshed"
	if strings.HasPrefix(refreshToken, "fake-refresh-") {
		id = strings.TrimPrefix(refreshToken, "fake-refresh-")
	}
	return &domain.TokenPair{
		AccessToken:  "fake-access-" + id,
		RefreshToken: refreshToken,
		ExpiresIn:    3600 * 24 * 60,
		UserID:       id,
		Username:     "threads_" + id,
		DisplayName:  "Threads " + id,
	}, nil
}

var _ domain.Provider = (*FakeProvider)(nil)
