package repository

import (
	"context"
	"encoding/json"
	"fmt"
	"strings"
	"time"

	app "haixun-backend/internal/library/errors"
	"haixun-backend/internal/library/errors/code"
	domrepo "haixun-backend/internal/model/threads_account/domain/repository"

	goredis "github.com/redis/go-redis/v9"
)

const oauthStateKeyPrefix = "threads_oauth_state:"
const oauthStateTTL = 10 * time.Minute

type oauthStateRedisStore struct {
	client *goredis.Client
}

func NewOAuthStateRedisStore(client *goredis.Client) domrepo.OAuthStateStore {
	return &oauthStateRedisStore{client: client}
}

func (s *oauthStateRedisStore) Save(ctx context.Context, state string, payload domrepo.OAuthStatePayload) error {
	if s.client == nil {
		return app.For(code.ThreadsAccount).DBUnavailable("Redis is not configured")
	}
	state = strings.TrimSpace(state)
	if state == "" {
		return app.For(code.ThreadsAccount).InputMissingRequired("oauth state is required")
	}
	raw, err := json.Marshal(payload)
	if err != nil {
		return err
	}
	return s.client.Set(ctx, oauthStateKeyPrefix+state, raw, oauthStateTTL).Err()
}

func (s *oauthStateRedisStore) Consume(ctx context.Context, state string) (*domrepo.OAuthStatePayload, error) {
	if s.client == nil {
		return nil, app.For(code.ThreadsAccount).DBUnavailable("Redis is not configured")
	}
	state = strings.TrimSpace(state)
	if state == "" {
		return nil, app.For(code.ThreadsAccount).InputMissingRequired("oauth state is required")
	}
	key := oauthStateKeyPrefix + state
	raw, err := s.client.GetDel(ctx, key).Bytes()
	if err == goredis.Nil {
		return nil, app.For(code.ThreadsAccount).ResNotFound("oauth state expired or invalid")
	}
	if err != nil {
		return nil, err
	}
	var payload domrepo.OAuthStatePayload
	if err := json.Unmarshal(raw, &payload); err != nil {
		return nil, fmt.Errorf("parse oauth state: %w", err)
	}
	if strings.TrimSpace(payload.TenantID) == "" || strings.TrimSpace(payload.OwnerUID) == "" {
		return nil, app.For(code.ThreadsAccount).InputInvalidFormat("oauth state payload incomplete")
	}
	return &payload, nil
}
