package sync

import (
	"fmt"
	"log"
	"path"

	"srs.epita.fr/fic-server/libfic"
)

var (
	ErrResolutionNotFound = fmt.Errorf("no resolution video or text file found")
)

type ThemeError struct {
	error
	ThemeId   int64
	ThemePath string
	ThemeName string
}

func NewThemeError(theme *fic.Theme, err error) *ThemeError {
	if theme == nil {
		return &ThemeError{
			error:     err,
			ThemePath: StandaloneExercicesDirectory,
		}
	}

	return &ThemeError{
		error:     err,
		ThemeId:   theme.Id,
		ThemePath: path.Base(theme.Path),
		ThemeName: theme.Name,
	}
}

func (e *ThemeError) Error() string {
	return fmt.Sprintf("%s: %s", e.ThemePath, e.error.Error())
}

func (e *ThemeError) GetError() error {
	return e.error
}

type ExerciceError struct {
	*ThemeError
	ExerciceId   int64
	ExercicePath string
	ExerciceName string
}

func NewExerciceError(exercice *fic.Exercice, err error, theme ...*fic.Theme) *ExerciceError {
	ltheme := len(theme)
	if ltheme > 1 {
		log.Fatal("Only 1 variadic arg is accepted in NewExerciceError")
		return nil
	} else if ltheme == 1 {
		return &ExerciceError{
			ThemeError:   NewThemeError(theme[0], err),
			ExerciceId:   exercice.Id,
			ExercicePath: path.Base(exercice.Path),
			ExerciceName: exercice.Title,
		}
	} else {
		return &ExerciceError{
			ThemeError:   &ThemeError{error: err},
			ExerciceId:   exercice.Id,
			ExercicePath: path.Base(exercice.Path),
			ExerciceName: exercice.Title,
		}
	}
}

func (e *ExerciceError) Error() string {
	return fmt.Sprintf("%s: %s", e.ExercicePath, e.ThemeError.error.Error())
}

type FileError struct {
	*ExerciceError
	Filename string
}

func NewFileError(exercice *fic.Exercice, filename string, err error, theme ...*fic.Theme) *FileError {
	return &FileError{
		ExerciceError: NewExerciceError(exercice, err, theme...),
		Filename:      filename,
	}
}

func (e *FileError) Error() string {
	return fmt.Sprintf("%s: file %q: %s", e.ExercicePath, e.Filename, e.ThemeError.error.Error())
}

type ChallengeTxtError struct {
	*ExerciceError
	ChallengeTxtLine uint
}

func NewChallengeTxtError(exercice *fic.Exercice, line uint, err error, theme ...*fic.Theme) *ChallengeTxtError {
	return &ChallengeTxtError{
		ExerciceError:    NewExerciceError(exercice, err, theme...),
		ChallengeTxtLine: line,
	}
}

func (e *ChallengeTxtError) Error() string {
	if e.ChallengeTxtLine != 0 {
		return fmt.Sprintf("%s:%d: %s", path.Join(e.ExercicePath, "challenge.toml"), e.ChallengeTxtLine, e.ThemeError.error.Error())
	} else {
		return fmt.Sprintf("%s: %s", path.Join(e.ExercicePath, "challenge.toml"), e.ThemeError.error.Error())
	}
}

type HintError struct {
	*ChallengeTxtError
	HintId    int
	HintTitle string
}

func NewHintError(exercice *fic.Exercice, hint *fic.EHint, line int, err error, theme ...*fic.Theme) *HintError {
	return &HintError{
		ChallengeTxtError: NewChallengeTxtError(exercice, 0, err, theme...),
		HintId:            line + 1,
		HintTitle:         hint.Title,
	}
}

func (e *HintError) Error() string {
	return fmt.Sprintf("%s: hint#%d (%s): %s", path.Join(e.ExercicePath, "challenge.toml"), e.HintId, e.HintTitle, e.ThemeError.error.Error())
}

type FlagError struct {
	*ChallengeTxtError
	FlagId    int
	FlagTitle string
}

func NewFlagError(exercice *fic.Exercice, flag *ExerciceFlag, line int, err error, theme ...*fic.Theme) *FlagError {
	return &FlagError{
		ChallengeTxtError: NewChallengeTxtError(exercice, 0, err, theme...),
		FlagId:            line,
	}
}

func (e *FlagError) Error() string {
	return fmt.Sprintf("%s: flag#%d: %s", path.Join(e.ExercicePath, "challenge.toml"), e.FlagId, e.ThemeError.error.Error())
}
