package main

import (
	"bytes"
	"database/sql"
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/google/uuid"
	"github.com/gorilla/handlers"
	_ "github.com/mattn/go-sqlite3"
)

type User struct {
	ID       int    `json:"id"`
	Email    string `json:"email"`
	Password string `json:"password"`
}

var db *sql.DB

func create_table(command string) {
	_, err := db.Exec(command)
	if err != nil {
		log.Fatal("Ошибка при создании таблицы:", err)
	}
	fmt.Println("База данных готова к работе.")
}

func initDB() {
	var err error
	dbFile := "UNITED.db"
	db, err = sql.Open("sqlite3", dbFile)
	if err != nil {
		log.Fatal("Ошибка подключения к базе данных:", err)
	}

	// Create tables if they don't exist
	create_table(`
	CREATE TABLE IF NOT EXISTS pay_urls (
		id TEXT PRIMARY KEY,
		url TEXT NOT NULL
	);`)

	create_table(`
	CREATE TABLE IF NOT EXISTS order_list (
		order_id TEXT PRIMARY KEY,
		total_price REAL NOT NULL,
		datetime TEXT NOT NULL
	);`)

	create_table(`CREATE TABLE IF NOT EXISTS order_lines (
    	line_id INTEGER PRIMARY KEY AUTOINCREMENT,
    	order_id TEXT NOT NULL,
		item_id INTEGER NOT NULL,
		item_title TEXT NOT NULL,
		item_price REAL NOT NULL,
		item_quantity INTEGER NOT NULL,
		FOREIGN KEY (order_id) REFERENCES order_list(order_id)
	);`)

	create_table(`CREATE TABLE IF NOT EXISTS users (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        email TEXT NOT NULL UNIQUE,
        password TEXT NOT NULL
    );`)

	fmt.Println("База данных готова к работе.")
}

/*----------------------------PAYMENT--------------------------------------*/
func payHandler(w http.ResponseWriter, r *http.Request) {

	fmt.Println("PAY HERE, BABY!")
	// Структура для тела запроса на localhost:5000
	type PaymentRequest struct {
		ID  string  `json:"id"`
		Sum float64 `json:"sum"`
	}

	// Структура для ответа от localhost:5000
	type PaymentResponse struct {
		Message string `json:"message"`
		URL     string `json:"redir_url"`
	}

	cookie, err := r.Cookie("totalPrice")
	if err != nil {
		fmt.Println("Не удалось получить amount из cookies")
		fmt.Println("\nPrinting all cookies")
		for _, c := range r.Cookies() {
			fmt.Println(c)
		}
		http.Error(w, "Не удалось получить amount из cookies", http.StatusBadRequest)
		return
	}

	// Преобразуем amount из строки в float64
	amount, err := strconv.ParseFloat(cookie.Value, 64)
	if err != nil {
		fmt.Println("Некорректное значение amount в cookies")
		http.Error(w, "Некорректное значение amount в cookies", http.StatusBadRequest)
		return
	}
	// Генерируем новый UUID
	newUUID := uuid.New().String()

	// Создаем тело запроса для localhost:5000
	paymentRequest := PaymentRequest{
		ID:  newUUID,
		Sum: amount,
	}

	// Преобразуем тело запроса в JSON
	requestBody, err := json.Marshal(paymentRequest)
	if err != nil {
		fmt.Println("Ошибка при создании запроса")
		http.Error(w, "Ошибка при создании запроса", http.StatusInternalServerError)
		return
	}

	// Отправляем POST-запрос на localhost:5000
	resp, err := http.Post("http://localhost:5000/pay", "application/json", bytes.NewBuffer(requestBody))
	if err != nil {
		fmt.Println("Ошибка при отправке запроса на сервер")
		http.Error(w, "Ошибка при отправке запроса на сервер", http.StatusInternalServerError)
		return
	}
	defer resp.Body.Close()

	// Читаем ответ от сервера
	var paymentResponse PaymentResponse
	if err := json.NewDecoder(resp.Body).Decode(&paymentResponse); err != nil {
		fmt.Println("Ошибка при чтении ответа от сервера")
		http.Error(w, "Ошибка при чтении ответа от сервера", http.StatusInternalServerError)
		return
	}

	// Сохраняем данные в базу данных
	insertSQL := `INSERT INTO pay_urls (uuid, url) VALUES (?, ?)`
	_, err = db.Exec(insertSQL, newUUID, paymentResponse.URL)
	if err != nil {
		http.Error(w, "Ошибка при сохранении данных в базу данных", http.StatusInternalServerError)
		return
	}

	// Формируем ответ клиенту в виде простого текста
	response := fmt.Sprintf("UUID: %s\nURL: %s", newUUID, paymentResponse.URL)

	// Set uuid cookie
	cookie = &http.Cookie{
		Name:    "uuid",
		Value:   newUUID,
		Expires: time.Now().Add(30 * time.Minute),
	}
	http.SetCookie(w, cookie)
	// Отправляем ответ в виде простого текста
	w.Header().Set("Content-Type", "text/plain")
	w.WriteHeader(http.StatusOK)
	w.Write([]byte(response))
}

func checkPayHandler(w http.ResponseWriter, r *http.Request) {
	// Извлекаем uuid из cookie
	cookie, err := r.Cookie("uuid")
	if err != nil {
		http.Error(w, "Не найден cookie с uuid", http.StatusBadRequest)
		return
	}
	uuid := cookie.Value
	if uuid == "" {
		http.Error(w, "Пустое значение uuid", http.StatusBadRequest)
		return
	}

	// Проверяем существование uuid в базе данных
	var exists bool
	querySQL := `SELECT EXISTS(SELECT 1 FROM pay_urls WHERE uuid = ?)`
	err = db.QueryRow(querySQL, uuid).Scan(&exists)
	if err != nil {
		http.Error(w, "Ошибка при проверке uuid в базе данных", http.StatusInternalServerError)
		return
	}

	if !exists {
		http.Error(w, "UUID не найден", http.StatusNotFound)
		return
	}

	// Создаем тело запроса для localhost:5000/check_pay
	checkRequest := struct {
		UUID string `json:"uuid"`
	}{
		UUID: uuid,
	}

	requestBody, err := json.Marshal(checkRequest)
	if err != nil {
		http.Error(w, "Ошибка при создании запроса", http.StatusInternalServerError)
		return
	}

	// Отправляем POST-запрос на localhost:5000/check_pay
	resp, err := http.Post("http://localhost:5000/check_pay", "application/json", bytes.NewBuffer(requestBody))
	if err != nil {
		http.Error(w, "Ошибка при отправке запроса на сервер", http.StatusInternalServerError)
		return
	}
	defer resp.Body.Close()

	// Читаем ответ от сервера
	var checkResponse struct {
		Mes string `json:"mes"`
	}
	if err := json.NewDecoder(resp.Body).Decode(&checkResponse); err != nil {
		http.Error(w, "Ошибка при чтении ответа от сервера", http.StatusInternalServerError)
		return
	}

	// Формируем ответ клиенту в зависимости от полученного результата
	var response string
	if checkResponse.Mes == "OK" {
		response = "OK"
	} else {
		response = "GTHO"
	}

	// Отправляем ответ в виде простого текста
	w.Header().Set("Content-Type", "text/plain")
	w.WriteHeader(http.StatusOK)
	w.Write([]byte(response))
}

func add_to_cart(w http.ResponseWriter, r *http.Request) {

	type CartItem struct {
		ID       int     `json:"id"`
		Title    string  `json:"title"`
		Price    float64 `json:"price"`
		Quantity int     `json:"quantity"`
	}

	type Cart struct {
		CartItems []CartItem `json:"cart_items"`
	}

	cartCookie, err := r.Cookie("cart")
	if err != nil {
		http.Error(w, "Cart cookie not found", http.StatusBadRequest)
		return
	}
	fmt.Print("cartCookie.Value: ", cartCookie.Value)
	cartValue := strings.ReplaceAll(cartCookie.Value, "'", "\"") // Replace single quotes with double quotes
	var cart Cart

	err = json.Unmarshal([]byte(cartValue), &cart)
	if err != nil {
		http.Error(w, "Invalid cart data format", http.StatusBadRequest)
		return
	}
	for _, item := range cart.CartItems {
		fmt.Println("Item ID:", item.ID)
		fmt.Println("Item Title:", item.Title)
		fmt.Println("Item Price:", item.Price)
		fmt.Println("Item Quantity:", item.Quantity)
	}
	del_cookie, err := r.Cookie("uuid")
	if err != nil {
		http.Error(w, "Не найден cookie с uuid", http.StatusBadRequest)
		return
	}
	total_cookie, err := r.Cookie("total_price")
	if err != nil {
		http.Error(w, "Не найден cookie с total_price", http.StatusBadRequest)
		return
	}

	total_price := total_cookie.Value
	order_id := del_cookie.Value
	fmt.Println("order_id: ", order_id)
	fmt.Println("total_price: ", total_price)

	insertSql := `INSERT INTO order_list VALUES (?, ?, ?)`
	_, err = db.Exec(insertSql, order_id, total_price, time.Now().Format("2006-01-02 15:04:05"))
	if err != nil {
		http.Error(w, "Ошибка при сохранении данных в базу данных 1", http.StatusInternalServerError)
		fmt.Print("Ошибка при сохранении данных в базу данных 1", err)
		return
	}

	for _, item := range cart.CartItems {

		insertSQL := `INSERT INTO order_lines (order_id, item_id, item_title, item_price, item_quantity) VALUES (?, ?, ?, ?, ?)`
		_, err = db.Exec(insertSQL, order_id, item.ID, item.Title, item.Price, item.Quantity)
		if err != nil {
			http.Error(w, "Ошибка при сохранении данных в базу данных 2", http.StatusInternalServerError)
			fmt.Println("Ошибка при сохранении данных в базу данных 2", err)
			return
		}
	}

	// insertSQL := `DELETE FROM url_pays WHERE uuid = ?`
	// _, err = db.Exec(insertSQL, del_cookie.Value)
	// if err != nil {
	// 	http.Error(w, "Ошибка при сохранении данных в базу данных", http.StatusInternalServerError)
	// 	return
	// }
	// // Clear uuid cookie
	// del_cookie = &http.Cookie{
	// 	Name:   "uuid",
	// 	Value:  "",
	// 	MaxAge: -1,
	// }
	// http.SetCookie(w, del_cookie)

	// Send success response
	w.WriteHeader(http.StatusOK)
	w.Write([]byte("OK"))
}

/*----------------------------USER-----------------------------------------*/
func registerHandler(w http.ResponseWriter, r *http.Request) {
	log.Printf("Received request with method: %s", r.Method)
	if r.Method != http.MethodPost {
		http.Error(w, "Метод не поддерживается", http.StatusMethodNotAllowed)
		return
	}

	var user User
	if err := json.NewDecoder(r.Body).Decode(&user); err != nil {
		http.Error(w, "Ошибка обработки JSON", http.StatusBadRequest)
		return
	}

	if user.Email == "" || user.Password == "" {
		http.Error(w, "Email и пароль обязательны", http.StatusBadRequest)
		return
	}

	insertQuery := `INSERT INTO users (email, password) VALUES (?, ?)`
	_, err := db.Exec(insertQuery, user.Email, user.Password)
	if err != nil {
		http.Error(w, "Ошибка записи в базу данных", http.StatusInternalServerError)
		fmt.Println("Ошибка:", err)
		return
	}

	w.WriteHeader(http.StatusCreated)
	json.NewEncoder(w).Encode(map[string]string{"message": "Пользователь успешно зарегистрирован", "email": user.Email})
}

func loginHandler(w http.ResponseWriter, r *http.Request) {
	log.Printf("Received request with method: %s", r.Method)
	if r.Method != http.MethodPost {
		http.Error(w, "Метод не поддерживается", http.StatusMethodNotAllowed)
		return
	}

	var user User
	if err := json.NewDecoder(r.Body).Decode(&user); err != nil {
		http.Error(w, "Ошибка обработки JSON", http.StatusBadRequest)
		return
	}

	if user.Email == "" || user.Password == "" {
		http.Error(w, "Email и пароль обязательны", http.StatusBadRequest)
		return
	}

	query := `SELECT id, email, password FROM users WHERE email = ? AND password = ?`
	var dbUser User
	err := db.QueryRow(query, user.Email, user.Password).Scan(&dbUser.ID, &dbUser.Email, &dbUser.Password)
	if err != nil {
		if err == sql.ErrNoRows {
			http.Error(w, "Неверные учетные данные", http.StatusUnauthorized)
		} else {
			http.Error(w, "Ошибка при проверке учетных данных", http.StatusInternalServerError)
		}
		return
	}

	w.WriteHeader(http.StatusOK)
	json.NewEncoder(w).Encode(map[string]string{"message": "Вход успешен", "email": dbUser.Email})
}

func getUsersHandler(w http.ResponseWriter, r *http.Request) {

	log.Printf("Received request with method: %s", r.Method)
	if r.Method != http.MethodGet {
		http.Error(w, "Метод не поддерживается", http.StatusMethodNotAllowed)
		return
	}

	rows, err := db.Query(`SELECT id, email, password FROM users`)
	if err != nil {
		http.Error(w, "Ошибка чтения из базы данных", http.StatusInternalServerError)
		return
	}
	defer rows.Close()

	var users []User
	for rows.Next() {
		var user User
		if err := rows.Scan(&user.ID, &user.Email, &user.Password); err != nil {
			http.Error(w, "Ошибка обработки данных", http.StatusInternalServerError)
			return
		}
		users = append(users, user)
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(users)
}

func main() {

	initDB()
	defer db.Close()
	// Setup CORS
	cors := handlers.CORS(
		handlers.AllowedOrigins([]string{"*"}),
		handlers.AllowedMethods([]string{"GET", "POST"}),
		handlers.AllowedHeaders([]string{"Content-Type"}),
	)

	// Register handlers
	http.HandleFunc("/api/pay", payHandler)
	http.HandleFunc("/api/check_pay", checkPayHandler)
	http.HandleFunc("/api/add_to_cart", add_to_cart)
	http.HandleFunc("/api/register", registerHandler)
	http.HandleFunc("/api/login", loginHandler)
	http.HandleFunc("/api/users", getUsersHandler)

	// Start server with CORS middleware
	port := os.Getenv("PORT")
	if port == "" {
		port = "8081"
	}

	fmt.Println("Go-сервер запущен на порту", port)
	log.Fatal(http.ListenAndServe(":"+port, cors(http.DefaultServeMux)))
}
