package models

import (
	"context"
	"database/sql"
	"fmt"

	"github.com/uptrace/bun"
	"github.com/uptrace/bun/dialect/sqlitedialect"
	"github.com/uptrace/bun/driver/sqliteshim"
)

type Persistence interface {
	CreateCapture(context.Context, *Capture) error
	GetCaptures(context.Context) ([]Capture, error)
	GetCapturesByID(context.Context, int64) (*Capture, error)
	DeleteCapture(context.Context, int64) error

	GetRecentMapScore(context.Context, int64) (*MapScore, error)
	CreateMapScore(context.Context, *MapScore) error

	CreateOrUpdateScores(context.Context, []Score) error
}

type SQLitePersistence struct {
	db *bun.DB
}

func NewSQLitePersistence(dbloc string) (*SQLitePersistence, error) {
	sqldb, err := sql.Open(sqliteshim.ShimName, fmt.Sprintf("file:%s", dbloc))
	if err != nil {
		return nil, err
	}

	db := bun.NewDB(sqldb, sqlitedialect.New())

	ctx := context.Background()

	for _, model := range []interface{}{(*Capture)(nil), (*MapScore)(nil), (*Score)(nil)} {
		_, err := db.NewCreateTable().Model(model).IfNotExists().Exec(ctx)
		if err != nil {
			return nil, err
		}
	}

	return &SQLitePersistence{
		db,
	}, nil
}

func (s *SQLitePersistence) GetCaptures(ctx context.Context) ([]Capture, error) {
	var captures []Capture
	err := s.db.NewSelect().Model(&captures).Scan(ctx)
	if err != nil {
		return nil, err
	}

	return captures, nil
}

func (s *SQLitePersistence) GetCapturesByID(ctx context.Context, id int64) (*Capture, error) {
	capture := Capture{
		ID: id,
	}

	err := s.db.NewSelect().Model(&capture).WherePK().Relation("MapScores").Relation("MapScores.ScoreList").Scan(ctx)

	return &capture, err
}

func (s *SQLitePersistence) DeleteCapture(ctx context.Context, id int64) error {
	capture := Capture{
		ID: id,
	}

	_, err := s.db.NewDelete().Model(&capture).WherePK().Exec(ctx)
	return err
}

func (s *SQLitePersistence) CreateCapture(ctx context.Context, capture *Capture) error {
	_, err := s.db.NewInsert().Model(capture).Exec(ctx)

	return err
}

func (s *SQLitePersistence) GetRecentMapScore(ctx context.Context, captureid int64) (*MapScore, error) {
	var scores []MapScore

	err := s.db.NewSelect().Model(&scores).Where("capture_id = ?", captureid).Limit(1).Order("start_time DESC").Scan(ctx)
	if err != nil {
		return nil, err
	}

	if len(scores) <= 0 {
		return nil, nil
	}

	return &scores[0], nil
}

func (s *SQLitePersistence) CreateMapScore(ctx context.Context, mapscore *MapScore) error {
	_, err := s.db.NewInsert().Model(mapscore).Exec(ctx)

	return err
}

func (s *SQLitePersistence) CreateOrUpdateScores(ctx context.Context, scores []Score) error {
	_, err := s.db.NewInsert().Model(&scores).On("CONFLICT (name, mapscore_id) DO UPDATE").Exec(ctx)
	return err
}
