package log

import (
    "fmt"
    "os"
    "time"

    "github.com/jmoiron/sqlx"
)

type (
    Logger struct {
        DB *sqlx.DB
    }

    Log struct {
        ID string `json:"id" db:"id"`
        Level LogLevel `json:"level" db:"level"`
        Type string `json:"type" db:"type"`
        Content string `json:"content" db:"content"`
        CreatedAt time.Time `json:"created_at" db:"created_at"`
    }
)

const (
    TYPE_ACCOUNT string = "account"
    TYPE_MUSIC string = "music"
    TYPE_ARTIST string = "artist"
    TYPE_BLOG string = "blog"
    TYPE_ARTWORK string = "artwork"
    TYPE_FILES string = "files"
    TYPE_MISC string = "misc"
    TYPE_CURSOR string = "cursor"
)

type LogLevel int
const (
    LEVEL_INFO LogLevel = 0
    LEVEL_WARN LogLevel = 1
)

const DEFAULT_LOG_PAGE_LENGTH = 25

func (self *Logger) Info(logType string, format string, args ...any) {
    logString := fmt.Sprintf(format, args...)
    fmt.Printf("[%s] [%s] INFO: %s\n", time.Now().Format(time.UnixDate), logType, logString)
    err := createLog(self.DB, LEVEL_INFO, logType, logString)
    if err != nil {
        fmt.Fprintf(os.Stderr, "WARN: Failed to push log to database: %v\n", err)
    }
}

func (self *Logger) Warn(logType string, format string, args ...any) {
    logString := fmt.Sprintf(format, args...)
    fmt.Fprintf(os.Stderr, "[%s] [%s] WARN: %s\n", time.Now().Format(time.UnixDate), logType, logString)
    err := createLog(self.DB, LEVEL_WARN, logType, logString)
    if err != nil {
        fmt.Fprintf(os.Stderr, "WARN: Failed to push log to database: %v\n", err)
    }
}

func (self *Logger) Fetch(id string) (*Log, error) {
    log := Log{}
    err := self.DB.Get(&log, "SELECT * FROM auditlog WHERE id=$1", id)
    return &log, err
}

func (self *Logger) Search(levelFilters []LogLevel, typeFilters []string, content string, limit int, offset int) ([]*Log, error) {
    logs := []*Log{}

    params := []any{ limit, offset }
    conditions := ""

    if len(content) > 0 {
      content = "%" + content + "%"
      conditions += " WHERE content LIKE $3"
      params = append(params, content)
    }

    if len(levelFilters) > 0 {
        if len(conditions) > 0 {
            conditions += " AND level IN ("
        } else {
            conditions += " WHERE level IN ("
        }
        for i := range levelFilters {
            conditions += fmt.Sprintf("$%d", len(params) + 1)
            if i < len(levelFilters) - 1 {
                conditions += ","
            }
            params = append(params, levelFilters[i])
        }
        conditions += ")"
    }

    if len(typeFilters) > 0 {
        if len(conditions) > 0 {
            conditions += " AND type IN ("
        } else {
            conditions += " WHERE type IN ("
        }
        for i := range typeFilters {
            conditions += fmt.Sprintf("$%d", len(params) + 1)
            if i < len(typeFilters) - 1 {
                conditions += ","
            }
            params = append(params, typeFilters[i])
        }
        conditions += ")"
    }

    query := fmt.Sprintf(
        "SELECT * FROM auditlog%s ORDER BY created_at DESC LIMIT $1 OFFSET $2",
        conditions,
    )

    /*
    fmt.Printf("%s (", query)
    for i, param := range params {
        fmt.Print(param)
        if i < len(params) - 1 {
            fmt.Print(", ")
        }
    }
    fmt.Print(")\n")
    */

    err := self.DB.Select(&logs, query, params...)
    if err != nil {
        return nil, err
    }
    return logs, nil
}

func createLog(db *sqlx.DB, logLevel LogLevel, logType string, content string) error {
    _, err := db.Exec(
        "INSERT INTO auditlog (level, type, content) VALUES ($1,$2,$3)",
        logLevel,
        logType,
        content,
    )
    return err
}
