package db

import (
	"context"
	"database/sql"
	_ "embed"
	"fmt"
	"log/slog"
	"time"

	"git.maximotejeda.com/maximo/tgb-user/internal/application/core/domain"
	_ "modernc.org/sqlite"
)

//go:embed schema.sql
var schema string

type Adapter struct {
	db  *sql.DB
	log *slog.Logger
}

func NewAdapter(dataSourceURL string) (*Adapter, error) {
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()
	log := slog.Default().With("adapter", "db")
	pragmas := "cache=shared&_foreign_keys=on&_busy_timeout=3000&_journal_mode=WAL"
	db, err := sql.Open("sqlite", fmt.Sprintf("file:%s?%s", dataSourceURL, pragmas))
	if err != nil {
		return nil, fmt.Errorf("connecion error: %w", err)
	}
	err = db.PingContext(ctx)
	if err != nil {
		return nil, fmt.Errorf("ping error: %w", err)
	}
	db.SetConnMaxIdleTime(10 * time.Second)
	CreateTables(db)

	return &Adapter{db: db, log: log}, nil
}

// Schema
func CreateTables(db *sql.DB) {
	_, err := db.Exec(schema)
	if err != nil {
		panic(err)
	}
}

func (a Adapter) Create(ctx context.Context, req *domain.User) (bool, error) {
	user := User{db: a.db, log: a.log}
	ok, err := user.Create(ctx, req)
	return ok, err
}
func (a Adapter) Get(ctx context.Context, id int64) (*domain.User, error) {
	user := NewUser(a.db, a.log)
	visit := NewVisits(a.db, a.log)

	us, err := user.Get(ctx, id)
	if err != nil {
		return nil, err
	}
	_, err1 := visit.Add(ctx, id)
	if err1 != nil {
		fmt.Println(err1)
	}
	return us, err
}
func (a Adapter) Edit(ctx context.Context, us *domain.User) (bool, error) {
	user := NewUser(a.db, a.log)
	ok, err := user.Edit(ctx, *us)
	return ok, err
}
func (a Adapter) Delete(ctx context.Context, id int64) (bool, error) {
	// TODO db work
	user := NewUser(a.db, a.log)
	ok, err := user.Delete(ctx, id)

	return ok, err
}
func (a Adapter) AddBot(ctx context.Context, tgb_id int64, botname string) (bool, error) {
	bu := NewBotUser(a.db, a.log)
	ok, err := bu.Add(ctx, tgb_id, botname)
	return ok, err
}
func (a Adapter) DeleteBot(ctx context.Context, tgb_id int64, botname string) (bool, error) {
	bu := NewBotUser(a.db, a.log)
	ok, err := bu.Delete(ctx, tgb_id, botname)
	return ok, err
}

func (a Adapter) GetBots(ctx context.Context, tgb_id int64) ([]*domain.Bot, error) {
	bu := NewBotUser(a.db, a.log)
	return bu.GetBots(ctx, tgb_id)
}

func (a Adapter) GetAllBotUsers(ctx context.Context, bot_name string) ([]*domain.User, error) {
	bu := NewBotUser(a.db, a.log)
	return bu.GetUsers(ctx, bot_name)
}

func (a Adapter) GetAllBots(ctx context.Context) ([]*domain.Bot, error) {
	bu := NewBotUser(a.db, a.log)
	return bu.GetAllBots(ctx)
}

// GetAccessRequest
// Returns all access request from a user
func (a Adapter) GetAccessRequest(ctx context.Context, tgbID int64) ([]*BotAccessRequest, error) {
	bar := NewBotAccessRequest(a.db, a.log)
	return bar.Get(ctx, tgbID)
}

// GetAllAccessRequest
// Returns all Access request from a bot
func (a Adapter) GetAllAccessRequest(ctx context.Context, botName string) ([]*BotAccessRequest, error) {
	bar := NewBotAccessRequest(a.db, a.log)
	return bar.GetAll(ctx, botName)
}

// CreateAccessRequest
// When user try get access to a restricted bot create a row in here
func (a Adapter) CreateAccessRequest(ctx context.Context, tgbID int64, botName string) (bool, error) {
	bar := NewBotAccessRequest(a.db, a.log)
	return bar.Add(ctx, tgbID, botName)
}

// GrantAccess
//
// If a row with user id and bot id is in db returns an object
func (a Adapter) GrantAccess(ctx context.Context, tgbID int64, botName string) (bool, error) {
	bar := NewBotAccessRequest(a.db, a.log)
	ok, err := bar.Delete(ctx, tgbID, botName)
	if !ok {
		return false, err
	} else {
		return a.AddBot(ctx, tgbID, botName)
	}
}

// GetBannedBot
// get banned bots for a user
func (a Adapter) GetBannedBot(ctx context.Context, tgbID int64) ([]*BotBannedAccess, error) {
	bba := NewBotBannedAccess(a.db, a.log)
	return bba.Get(ctx, tgbID)
}

// GetAllBannedUsers
// get all users banned from a bot
func (a Adapter) GetAllBannedUsers(ctx context.Context, botName string) ([]*BotBannedAccess, error) {
	bba := NewBotBannedAccess(a.db, a.log)
	return bba.GetAll(ctx, botName)
}

// BanUser
// ban a user creating a row in db ban and removin access request and bot grants
func (a Adapter) BanUser(ctx context.Context, tgbID, until int64, botName string) (bool, error) {
	bba := NewBotBannedAccess(a.db, a.log)
	bar := NewBotAccessRequest(a.db, a.log)
	if ok, err := bar.Delete(ctx, tgbID, botName); !ok {
		a.log.Error("access request not deleted", "error", err)
	}
	if ok, err := a.DeleteBot(ctx, tgbID, botName); !ok {
		a.log.Error("bot access not deleted", "error", err)
	}

	return bba.Add(ctx, tgbID, until, botName)

}

// UnBanUser
// Remove restriction from an user and create a access request
func (a Adapter) UnBanUser(ctx context.Context, tgbID int64, botName string) (bool, error) {
	bba := NewBotBannedAccess(a.db, a.log)
	bar := NewBotAccessRequest(a.db, a.log)

	if ok, err := bba.Delete(ctx, tgbID, botName); !ok {
		return false, err
	} else {
		return bar.Add(ctx, tgbID, botName)
	}
}

// CreateBot
// create a new bot in db
func (a Adapter) CreateBot(ctx context.Context, botName string) (bool, error) {
	bu := NewBotUser(a.db, a.log)
	return bu.CreateBot(ctx, botName)
}
