package main

import (
	"encoding/json"
	"fmt"
	"log"
	"strings"
	"time"

	"github.com/boltdb/bolt"
	"github.com/sergi/go-diff/diffmatchpatch"
)

var db *bolt.DB
var open bool

// Open to create the database and open
func Open(filename string) error {
	var err error
	config := &bolt.Options{Timeout: 30 * time.Second}
	db, err = bolt.Open(filename, 0600, config)
	if err != nil {
		fmt.Println("Opening BoltDB timed out")
		log.Fatal(err)
	}
	open = true
	return nil
}

// Close database
func Close() {
	open = false
	db.Close()
}

// WikiData is data for storing in DB
type WikiData struct {
	Title       string
	CurrentText string
	Diffs       []string
	Timestamps  []string
}

func hasPassword(title string) (bool, error) {
	title = strings.ToLower(title)
	if !open {
		return false, fmt.Errorf("db must be opened before loading")
	}
	hasPassword := false
	err := db.View(func(tx *bolt.Tx) error {
		var err error
		b := tx.Bucket([]byte("datas"))
		if b == nil {
			return fmt.Errorf("db must be opened before loading")
		}
		k := []byte(title)
		val := b.Get(k)
		if val == nil {
			return nil
		}
		var p WikiData
		err = p.decode(val)
		if err != nil {
			return err
		}
		for _, line := range strings.Split(p.CurrentText, "\n") {
			if strings.Contains(line, "<") == true && strings.Contains(line, ">") == true && strings.Contains(line, "user") == true && strings.Contains(line, "password") == true && strings.Contains(line, "public") == true {
				hasPassword = true
			}
		}
		return nil
	})
	if err != nil {
		fmt.Printf("Could not get WikiData: %s", err)
		return false, err
	}
	return hasPassword, nil
}

func getCurrentText(title string, version int) (string, []versionsInfo, bool, time.Duration) {
	title = strings.ToLower(title)
	var vi []versionsInfo
	totalTime := time.Now().Sub(time.Now())
	isCurrent := true
	currentText := ""
	if !open {
		return currentText, vi, isCurrent, totalTime
	}
	err := db.View(func(tx *bolt.Tx) error {
		var err error
		b := tx.Bucket([]byte("datas"))
		if b == nil {
			return fmt.Errorf("db must be opened before loading")
		}
		k := []byte(title)
		val := b.Get(k)
		if val == nil {
			return nil
		}
		var p WikiData
		err = p.decode(val)
		if err != nil {
			return err
		}
		currentText = p.CurrentText
		if version > -1 && version < len(p.Diffs) {
			// get that version of text instead
			currentText = rebuildTextsToDiffN(p, version)
			isCurrent = false
		}
		vi, totalTime = getImportantVersions(p)
		log.Println(totalTime)
		return nil
	})
	if err != nil {
		fmt.Printf("Could not get WikiData: %s", err)
	}
	return currentText, vi, isCurrent, totalTime
}

func (p *WikiData) load(title string) error {
	title = strings.ToLower(title)
	if !open {
		return fmt.Errorf("db must be opened before loading!")
	}
	err := db.View(func(tx *bolt.Tx) error {
		var err error
		b := tx.Bucket([]byte("datas"))
		if b == nil {
			return nil
		}
		k := []byte(title)
		val := b.Get(k)
		if val == nil {
			// make new one
			p.Title = title
			p.CurrentText = ""
			p.Diffs = []string{}
			p.Timestamps = []string{}
			return nil
		}
		err = p.decode(val)
		if err != nil {
			return err
		}
		return nil
	})
	if err != nil {
		fmt.Printf("Could not get WikiData: %s", err)
		return err
	}
	return nil
}

func (p *WikiData) save(newText string) error {
	if !open {
		return fmt.Errorf("db must be opened before saving")
	}
	err := db.Update(func(tx *bolt.Tx) error {
		bucket, err := tx.CreateBucketIfNotExists([]byte("datas"))
		if err != nil {
			return fmt.Errorf("create bucket: %s", err)
		}
		// find diffs
		dmp := diffmatchpatch.New()
		diffs := dmp.DiffMain(p.CurrentText, newText, true)
		delta := dmp.DiffToDelta(diffs)
		p.CurrentText = newText
		p.Timestamps = append(p.Timestamps, time.Now().Format(time.ANSIC))
		p.Diffs = append(p.Diffs, delta)
		enc, err := p.encode()
		if err != nil {
			return fmt.Errorf("could not encode WikiData: %s", err)
		}
		err = bucket.Put([]byte(p.Title), enc)
		return err
	})
	return err
}

func (p *WikiData) encode() ([]byte, error) {
	enc, err := json.Marshal(p)
	if err != nil {
		return nil, err
	}
	return enc, nil
}

func (p *WikiData) decode(data []byte) error {
	err := json.Unmarshal(data, &p)
	if err != nil {
		return err
	}
	return nil
}