package repository

import (
	"context"
	"database/sql"
	"errors"
	"time"

	dbgen "Yimaru-Backend/gen/db"
	"Yimaru-Backend/internal/domain"
	"Yimaru-Backend/internal/ports"
	"Yimaru-Backend/internal/services/authentication"

	"github.com/jackc/pgx/v5/pgtype"
)

// NewTokenStore returns a TokenStore implementation
func NewTokenStore(s *Store) ports.TokenStore {
	return s
}

// CreateRefreshToken inserts a new refresh token into the database
func (s *Store) CreateRefreshToken(ctx context.Context, rt domain.RefreshToken) error {
	// Use provided ExpiresAt and CreatedAt when available; otherwise fall back to sensible defaults.
	if rt.ExpiresAt.IsZero() {
		rt.ExpiresAt = time.Now().Add(10 * time.Minute)
	}
	createdAt := rt.CreatedAt
	if createdAt.IsZero() {
		createdAt = time.Now()
	}

	return s.queries.CreateRefreshToken(ctx, dbgen.CreateRefreshTokenParams{
		UserID: rt.UserID,
		Token:  rt.Token,
		ExpiresAt: pgtype.Timestamptz{
			Time:  rt.ExpiresAt,
			Valid: true,
		},
		CreatedAt: pgtype.Timestamptz{
			Time:  createdAt,
			Valid: true,
		},
		Revoked: rt.Revoked,
	})
}

// GetRefreshToken retrieves a refresh token by its token string
func (s *Store) GetRefreshToken(ctx context.Context, token string) (domain.RefreshToken, error) {
	rf, err := s.queries.GetRefreshToken(ctx, token)
	if err != nil {
		if errors.Is(err, sql.ErrNoRows) {
			return domain.RefreshToken{}, authentication.ErrRefreshTokenNotFound
		}
		return domain.RefreshToken{}, err
	}

	return domain.RefreshToken{
		Token:     rf.Token,
		UserID:    rf.UserID,
		CreatedAt: rf.CreatedAt.Time,
		ExpiresAt: rf.ExpiresAt.Time,
		Revoked:   rf.Revoked,
	}, nil
}

// GetRefreshTokenByUserID retrieves a refresh token for a specific user
func (s *Store) GetRefreshTokenByUserID(ctx context.Context, id int64) (domain.RefreshToken, error) {
	rf, err := s.queries.GetRefreshTokenByUserID(ctx, id)
	if err != nil {
		if errors.Is(err, sql.ErrNoRows) {
			return domain.RefreshToken{}, authentication.ErrRefreshTokenNotFound
		}
		return domain.RefreshToken{}, err
	}

	return domain.RefreshToken{
		Token:     rf.Token,
		UserID:    rf.UserID,
		CreatedAt: rf.CreatedAt.Time,
		ExpiresAt: rf.ExpiresAt.Time,
		Revoked:   rf.Revoked,
	}, nil
}

// RevokeRefreshToken marks a refresh token as revoked
func (s *Store) RevokeRefreshToken(ctx context.Context, token string) error {
	return s.queries.RevokeRefreshToken(ctx, token)
}

// GetUserByEmailOrPhone retrieves a user by email or phone number and optional organization ID
func (s *Store) GetUserByEmailOrPhone(
	ctx context.Context,
	email string,
	phone string,
) (domain.User, error) {

	u, err := s.queries.GetUserByEmailPhone(ctx, dbgen.GetUserByEmailPhoneParams{
		Email: pgtype.Text{
			String: email,
			Valid:  email != "",
		},
		PhoneNumber: pgtype.Text{
			String: phone,
			Valid:  phone != "",
		},
		// OrganizationID: pgtype.Int8{Int64: organizationID},
	})
	if err != nil {
		if errors.Is(err, sql.ErrNoRows) {
			return domain.User{}, authentication.ErrUserNotFound
		}
		return domain.User{}, err
	}

	var lastLogin *time.Time
	if u.LastLogin.Valid {
		lastLogin = &u.LastLogin.Time
	}

	var updatedAt *time.Time
	if u.UpdatedAt.Valid {
		updatedAt = &u.UpdatedAt.Time
	}

	return domain.User{
		ID:        u.ID,
		FirstName: u.FirstName.String,
		LastName:  u.LastName.String,
		// UserName:    u.UserName,
		Email:       u.Email.String,
		PhoneNumber: u.PhoneNumber.String,
		Password:    u.Password,
		Role:        domain.Role(u.Role),

		AgeGroup:       u.AgeGroup.String,
		EducationLevel: u.EducationLevel.String,
		Country:        u.Country.String,
		Region:         u.Region.String,

		EmailVerified: u.EmailVerified,
		PhoneVerified: u.PhoneVerified,
		Status:        domain.UserStatus(u.Status),

		LastLogin:         lastLogin,
		ProfileCompleted:  u.ProfileCompleted.Bool,
		ProfilePictureURL: u.ProfilePictureUrl.String,
		PreferredLanguage: u.PreferredLanguage.String,

		// OrganizationID: domain.ValidInt64{
		// 	Value: u.OrganizationID.Int64,
		// 	Valid: u.OrganizationID.Valid,
		// },
		CreatedAt: u.CreatedAt.Time,
		UpdatedAt: updatedAt,
	}, nil
}
