package main

import (
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"html/template"
	"net/http"
	"os"
	"os/exec"
	"strings"

	"github.com/gorilla/mux"
	log "github.com/sirupsen/logrus"
	"golang.org/x/text/language"
	"golang.org/x/text/message"
)

const page = `<!DOCTYPE html>
<html>

<head>
    <title>{{ .PageTitle }}</title>

    <meta charset="utf-8">
    <meta http-equiv="X-UA-COMPATIBLE" content="IE=edge">
    <meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
    <link rel="shortcut icon" type="image/png" href="/static/adminlte/images/favicon.png" />

    <link rel="stylesheet" type="text/css" href="/static/adminlte/plugins/fontawesome-free/css/all.min.css">
    <link rel="stylesheet" type="text/css" href="/static/adminlte/additions/ionicons.min.css">
    <link rel="stylesheet" type="text/css" href="/static/adminlte/plugins/jquery-ui/jquery-ui.css">
    <link rel="stylesheet" type="text/css" href="/static/adminlte/plugins/ion-rangeslider/css/ion.rangeSlider.min.css">
    <link rel="stylesheet" type="text/css" href="/static/adminlte/plugins/bootstrap-slider/css/bootstrap-slider.min.css">
    <link rel="stylesheet" type="text/css" href="/static/adminlte/plugins/icheck-bootstrap/icheck-bootstrap.min.css">
    <link rel="stylesheet" type="text/css" href="/static/adminlte/plugins/daterangepicker/daterangepicker.css">
    <link rel="stylesheet" type="text/css" href="/static/adminlte/plugins/toastr/toastr.min.css">
    <link rel="stylesheet" type="text/css" href="/static/adminlte/plugins/select2/css/select2.min.css">
    <link rel="stylesheet" type="text/css" href="/static/adminltemod/plugins/datatables.min.css" />
    <link rel="stylesheet" type="text/css" href="/static/adminltemod/css/armaDatatables.css" />
    <link rel="stylesheet" type="text/css" href="/static/adminltemod/css/bootstrap-dialog.css" />
    <link rel="stylesheet" type="text/css" href="/static/adminlte/dist/css/adminlte.css">
    <link rel="stylesheet" type="text/css" href="/static/adminltemod/css/adminltemod.css">
    <link rel="stylesheet" type="text/css" href="/static/adminlte/additions/googleapis.css">
    <link rel="stylesheet" type="text/css" href="/static/adminltemod/css/bootstrap-datetimepicker.css">

</head>

<body class="hold-transition login-page" style="height: auto;">

    <div class="login-box">
        <div class="login-logo">
            <a href="#"><img class="w-100" src="/static/adminlte/images/logo_lg.svg" alt="logo" ></a>
        </div>

        <div class="card">
            <div class="card-body login-card-body">
                <div class="row">
                    <div class="col">
                        <p class="login-box-msg">{{ .CardTitle }}</p>
                    </div>
                </div>
                <div class="row">
                    <div class="col">
                        <div class="progress">
                            <div id="progress" class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" style="width: 10%" aria-valuenow="10"
                                aria-valuemin="0" aria-valuemax="100"></div>
                        </div>
                    </div>
                </div>
                <div class="row">
                    <div class="col" style="margin-top: 1em;">
                        <button class="btn btn-primary .btn-sm" type="button" data-toggle="collapse" data-target="#collapseExample" aria-expanded="false" aria-controls="collapseExample">
                            {{ .Details }}
                          </button>
                    </div>
                </div>
                <div class="collapse" id="collapseExample">
                    <div class="row">
                        <div class="col">
                            <table id="serviceList" class="table table-hover dataTable">
                                <thead>
                                    <th>{{ .ServiceName }}</th>
                                    <th>{{ .Status }}</th>
                                    <th>{{ .SubStatus }}</th>
                                </thead>
                                <tbody></tbody>
                            </table>
                        </div>
                    </div>
                </div>
            </div>
            <!-- /.login-card-body -->
        </div>
    </div>
    <!-- /.login-box -->
    <script src="/static/adminlte/plugins/jquery/jquery.min.js"></script>
    <script src="/static/adminlte/plugins/bootstrap/js/bootstrap.bundle.min.js "></script>
    <script src="/static/adminlte/plugins/moment/moment-with-locales.min.js"></script>
    <script src="/static/adminlte/plugins/inputmask/min/jquery.inputmask.bundle.min.js"></script>
    <script src="/static/adminlte/plugins/bs-custom-file-input/bs-custom-file-input.min.js "></script>
    <script src="/static/adminlte/plugins/bootstrap-switch/js/bootstrap-switch.min.js"></script>
    <script src="/static/adminlte/plugins/select2/js/select2.full.min.js "></script>

    <script src="/static/adminlte/plugins/toastr/toastr.min.js"></script>
    <script src="/static/adminlte/plugins/jquery-ui/jquery-ui.min.js"></script>
    <script src="/static/adminltemod/plugins/daterangepicker/daterangepicker.js"></script>
    <script src="/static/adminlte/plugins/ion-rangeslider/js/ion.rangeSlider.min.js"></script>
    <script src="/static/adminlte/plugins/bootstrap-slider/bootstrap-slider.min.js"></script>
    <script src="/static/adminltemod/js/overlay.js"></script>
    <!-- Resolve conflict in jQuery UI tooltip with Bootstrap tooltip -->
    <script>
        $.widget.bridge('uibutton', $.ui.button);
    </script>
    <script src="/static/adminltemod/js/bootstrap-dialog.js"></script>
    <script src="/static/adminltemod/js/bootstrap3-typeahead.js"></script>
    <script src="/static/adminltemod/js/multitypeahead.js"></script>

    <script src="/static/adminltemod/plugins/datatables.min.js"></script>
    <script src="/static/adminltemod/js/infoTable.js"></script>

    <script src="/static/adminlte/dist/js/adminlte.js"></script>
    <script src="/static/adminltemod/js/adminltemod.js"></script>
    <script src="/static/adminltemod/js/bootstrap-dialog-trans.js"></script>
    <script src="/static/adminltemod/js/quick_edit.js"></script>
    <!-- timepicker -->
    <script type="text/javascript" src="/static/adminltemod/js/bootstrap-datetimepicker.js"></script>
    <script type="text/javascript" src="/static/adminltemod/js/jquery.collapser.min.js"></script>

    <script type="text/javascript">
        $(document).ready(function () {
            let table = $('#serviceList').DataTable({
                ajax: {
                    url: window.location.origin + "/state/",
                    dataSrc: "items"
                },
                paging: false,
                info: false,
                searching: false,
                ordering: false,
                drawCallback: function (settings) {
                    let total = $('#serviceList').find('tr').length-1;
                    let good = $('#serviceList').find('.badge-count').length;

                    $("#progress").attr("aria-valuemax", total);
                    $("#progress").attr("aria-valuenow", good);
                    $("#progress").attr("style", "width: " + (good * 100) / total + "%");

                    // Need to check that we can get login page
                    if (total === good) {
                        $.ajax({
                            url: window.location.origin,
                            complete: function (xhr, textStatus) {
                                if (xhr.status == 200) {
                                    window.location.replace(window.location.origin);
                                }
                            }
                        });
                    }
                },
                columns: [
                    {
                        data: "name",
                        render: function (data, type, row, meta) {
                            return data.split(".")[0].replace("amc", "");;
                        }
                    },
                    {
                        data: "active_state",
                        render: function (data, type, row, meta) {
                            var css_class = "badge-danger";
                            if (data === "active") {
                                css_class = "badge-success badge-count";
                            } else if (data === "activating") {
                                css_class = "badge-warning";
                            } else if (data === "inactive") {
                                css_class = "bg-warning disabled";
                            }
                            return '<span class="badge ' + css_class +'"">' + data +'</span>';
                        }
                    },
                    {
                        data: "sub_state",
                        render: function (data, type, row, meta) {
                            var css_class = "badge-danger";
                            if (data === "running") {
                                css_class = "badge-success";
                            } else if (data === "start"){
                                css_class = "badge-warning";
                            } else if (data === "dead") {
                                css_class = "bg-warning disabled";
                            }
                            return '<span class="badge ' + css_class +'"">' + data +'</span>';
                        }
                    }
                ]
            });

            setInterval(function () {
                table.ajax.reload();
            }, 1000);
        });
    </script>
</body>

</html>`

// Context for rendering template
type Context struct {
	PageTitle   string
	CardTitle   string
	Details     string
	ServiceName string
	Status      string
	SubStatus   string
}

type Item struct {
	Name     string `json:"name"`
	State    string `json:"active_state"`
	SubState string `json:"sub_state"`
}

type Response struct {
	Status string `json:"status"`
	Reason string `json:"reason"`
	Items  []Item `json:"items"`
}

func (response Response) Send(w http.ResponseWriter) {
	bytes, err := json.Marshal(response)
	if err != nil {
		log.Errorf("Can't serialize stat: %v\n", err.Error())
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	w.Header().Set("Content-Type", "application/json")
	w.Write(bytes)
}

const (
	StatusOK  = "ok"
	StatusErr = "error"
)

func checkService(w http.ResponseWriter, r *http.Request) {
	var response Response

	items, err := checkServices()
	if err != nil {
		log.Errorf("Can't get status: %v\n", err.Error())

		response.Status = StatusErr
		response.Reason = err.Error()
		response.Send(w)
		return
	}

	response.Status = StatusOK
	response.Items = items

	response.Send(w)
}

func renderPage(w http.ResponseWriter, r *http.Request) {
	t, _, err := language.ParseAcceptLanguage(r.Header.Get("Accept-Language"))
	if err != nil {
		log.Errorf("Got error parsing Accept-Language header: %v", err.Error())
	}
	log.Infof("Got languages: %v", t)

	templ, err := template.New("page").Parse(page)
	if err != nil {
		log.Errorf("Can't parse template: %v", err.Error())
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	var print *message.Printer
	if len(t) > 0 {
		for _, cur := range t {
			if fmt.Sprintf("%v", cur)[:2] == fmt.Sprintf("%v", language.Russian) {
				log.Debugf("Creating printer for lang %v", cur)
				print = message.NewPrinter(cur)
				break
			}
		}
	}

	if print == nil {
		log.Infof("Create default printer")
		print = message.NewPrinter(language.English)
	}

	context := Context{
		PageTitle:   print.Sprintf("PageTitle", "Loading"),
		CardTitle:   print.Sprintf("CardTitle", "Please wait, services are loading"),
		Details:     print.Sprintf("Details", "Show details"),
		ServiceName: print.Sprintf("ServiceName", "Service name"),
		Status:      print.Sprintf("Status", "Status"),
		SubStatus:   print.Sprintf("SubStatus", "Sub status"),
	}

	var buf bytes.Buffer
	err = templ.Execute(&buf, context)

	if err != nil {
		log.Errorf("Can't render template: %v", err.Error())
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	w.Write(buf.Bytes())
}

func httpMatch(r *http.Request, rm *mux.RouteMatch) bool {
	// This check prevent non localhost requests
	if r.RemoteAddr[:5] == "[::1]" {
		return true
	} else if r.RemoteAddr[:9] == "127.0.0.1" {
		return true
	} else if r.RemoteAddr[:9] == "localhost" {
		return true
	}
	log.Error("Bad remote addr")

	return false
}

func init() {
	message.SetString(language.Russian, "PageTitle", "Загрузка")
	message.SetString(language.Russian, "CardTitle", "Пожалуйста, подождите, сервисы загружаются")
	message.SetString(language.Russian, "Details", "Подробности")
	message.SetString(language.Russian, "ServiceName", "Сервис")
	message.SetString(language.Russian, "Status", "Статус")
	message.SetString(language.Russian, "SubStatus", "Подстатус")

	message.SetString(language.English, "PageTitle", "Loading")
	message.SetString(language.English, "CardTitle", "Please wait, services are loading")
	message.SetString(language.English, "Details", "Show details")
	message.SetString(language.English, "ServiceName", "Service name")
	message.SetString(language.English, "Status", "Status")
	message.SetString(language.English, "SubStatus", "Sub status")
}

// Services to check it's state
var services = [...]string{"amccore.service", "amccelery.service", "amccelerybeat.service", "amccorrelator.service", "amcclient.service", "elasticsearch.service", "amcvector.service"}

func checkServices() ([]Item, error) {
	result := make([]Item, 0)

	for _, service := range services {
		// Get service status
		var out1 bytes.Buffer
		cmd1 := exec.Command("systemctl", "show", "-p", "ActiveState", "--value", service)
		cmd1.Stdout = &out1
		err := cmd1.Run()
		if err != nil {
			log.Errorf("Can't get service active state: %v", err.Error())
			return nil, err
		}

		var out2 bytes.Buffer
		cmd2 := exec.Command("systemctl", "show", "-p", "SubState", "--value", service)
		cmd2.Stdout = &out2
		err = cmd2.Run()
		if err != nil {
			log.Errorf("Can't get service sub state: %v", err.Error())
			return nil, err
		}

		result = append(result, Item{
			Name:     service,
			State:    strings.Replace(out1.String(), "\n", "", 1),
			SubState: strings.Replace(out2.String(), "\n", "", 1),
		})
	}

	return result, nil
}

func main() {
	log.SetFormatter(&log.TextFormatter{})
	log.SetOutput(os.Stdout)

	port := flag.Int("port", 9080, "Port for work")
	flag.Parse()

	log.Info("Starting")
	router := mux.NewRouter()
	router.HandleFunc("/", checkService).Methods("GET").MatcherFunc(httpMatch)
	router.HandleFunc("/page", renderPage).Methods("GET").MatcherFunc(httpMatch)

	http.ListenAndServe(fmt.Sprintf(":%v", *port), router)
}
