package main

import (
	"context"
	"database/sql"
	"encoding/json"
	"fmt"
	"io"
	"time"
)

type Games struct {
	db DB
}

func NewGames(ctx context.Context, db DB) (Games, error) {
	err := db.Exec(ctx, `
		CREATE TABLE IF NOT EXISTS users (
			uuid TEXT,
         updated DATETIME,
			name TEXT
		);
		CREATE TABLE IF NOT EXISTS games (
			uuid TEXT,
         updated DATETIME,
			name TEXT,
         completed DATETIME
		);
		CREATE TABLE IF NOT EXISTS players (
			user_uuid TEXT,
			game_uuid TEXT,
         updated DATETIME
		);
		CREATE TABLE IF NOT EXISTS events (
			game_uuid TEXT,
			updated DATETIME,
			payload TEXT
		);
	`)
	return Games{db: db}, err
}

func (games Games) GamesForUser(ctx context.Context, id string) ([]string, error) {
	result := []string{}
	err := games.db.Query(ctx, func(rows *sql.Rows) error {
		var game string
		err := rows.Scan(&game)
		result = append(result, game)
		return err
	}, `
		SELECT players.game_uuid
		FROM players
		WHERE players.user_uuid=? 
	`, id)
	return result, err
}

func (games Games) GameByName(ctx context.Context, name string) (string, error) {
	return "", io.EOF
}

type (
	GameState struct {
		Completed time.Time
		Players   map[string]PlayerState
	}

	PlayerState struct {
		Kills     []Kill
		KillWords KillWords
	}

	Kill struct {
		Timestamp time.Time
		Victim    string
		Public    bool
	}

	KillWords struct {
		Global     string
		Assigned   time.Time
		Assignee   string
		Assignment Assignment
	}

	Assignment struct {
		Public  []string
		Private []string
	}

	EventType int

	EventPlayerJoin struct {
		ID string
	}
	EventPlayerLeave struct {
		ID string
	}
	EventGameComplete       struct{}
	EventAssignmentRotation struct {
		Killer      string
		Killed      string
		Assignments map[string]Assignment
	}
)

const (
	PlayerJoin EventType = iota + 1
	PlayerLeave
	GameComplete
	AssignmentRotation
)

func (games Games) GameState(ctx context.Context, id string) (GameState, error) {
	result := GameState{Players: map[string]PlayerState{}}

	err := games.db.Query(ctx, func(rows *sql.Rows) error {
		var timestamp time.Time
		var payload []byte
		if err := rows.Scan(&timestamp, &payload); err != nil {
			return err
		}

		var peek struct {
			Type EventType
		}
		if err := json.Unmarshal(payload, &peek); err != nil {
			return err
		}

		switch peek.Type {
		case PlayerJoin:
			var playerJoin EventPlayerJoin
			if err := json.Unmarshal(payload, &playerJoin); err != nil {
				return err
			}
			result.Players[playerJoin.ID] = PlayerState{}
			return nil
		case PlayerLeave:
			var playerLeave EventPlayerLeave
			if err := json.Unmarshal(payload, &playerLeave); err != nil {
				return err
			}
			delete(result.Players, playerLeave.ID)
			return nil
		case GameComplete:
			var gameComplete EventGameComplete
			if err := json.Unmarshal(payload, &gameComplete); err != nil {
				return err
			}
			result.Completed = timestamp
			return nil
		case AssignmentRotation:
			var assignmentRotation EventAssignmentRotation
			if err := json.Unmarshal(payload, &assignmentRotation); err != nil {
				return err
			}
			// TODO gather current assignees
			// TODO get victim's target
			// TODO assign victim's target to killer
			// TODO randomize everyone else so not the same as before
			return fmt.Errorf("not impl: assignment rotation: %+v", assignmentRotation)
		default:
			return fmt.Errorf("unknown event type %d: %s", peek.Type, payload)
		}

		return nil
	}, `
		SELECT timestamp, payload
		FROM events
      WHERE game_uuid=?
	`, id)

	return result, err
}
