package raid

import (
	"fmt"
	"os/exec"
	"strconv"
	"strings"
	"time"
)

// RAIDInfo represents information about a RAID array.
type RAIDInfo struct {
	DevicePath     string
	Version        string
	CreationTime   time.Time
	RaidLevel      string
	ArraySize      int
	UsedDevSize    int
	RaidDevices    int
	TotalDevices   int
	Persistence    string
	UpdateTime     time.Time
	State          string
	ActiveDevices  int
	WorkingDevices int
	FailedDevices  int
	SpareDevices   int
	Consistency    string
	RebuildStatus  string
	Name           string
	UUID           string
	Events         int
	DeviceInfo     []DeviceInfo
}

// DeviceInfo represents information about a device in a RAID array.
type DeviceInfo struct {
	State      []string
	DevicePath string
	RaidDevice int //Sequence of the raid device?
}

// GetRAIDInfo retrieves information about a RAID array using the mdadm command.
// arrayName must be in full path (e.g. /dev/md0)
func (m *Manager) GetRAIDInfo(arrayName string) (*RAIDInfo, error) {
	cmd := exec.Command("sudo", "mdadm", "--detail", arrayName)

	output, err := cmd.Output()
	if err != nil {
		return nil, fmt.Errorf("error running mdadm command: %v", err)
	}

	info := parseRAIDInfo(string(output))

	//Fill in the device path so other service can use it more easily
	info.DevicePath = arrayName
	return info, nil
}

// parseRAIDInfo parses the output of mdadm --detail command and returns the RAIDInfo struct.
func parseRAIDInfo(output string) *RAIDInfo {
	lines := strings.Split(output, "\n")

	raidInfo := &RAIDInfo{}
	for _, line := range lines {
		fields := strings.Fields(line)
		if len(fields) >= 2 {
			switch fields[0] {
			case "Version":
				raidInfo.Version = fields[2]
			case "Creation":
				creationTimeStr := strings.Join(fields[3:], " ")
				creationTime, _ := time.Parse(time.ANSIC, creationTimeStr)
				raidInfo.CreationTime = creationTime
			case "Raid":
				if fields[1] == "Level" {
					//Raid Level
					raidInfo.RaidLevel = fields[3]
				} else if fields[1] == "Devices" {
					raidInfo.RaidDevices, _ = strconv.Atoi(fields[3])
				}
			case "Array":
				raidInfo.ArraySize, _ = strconv.Atoi(fields[3])
			case "Used":
				raidInfo.UsedDevSize, _ = strconv.Atoi(fields[4])
			case "Total":
				raidInfo.TotalDevices, _ = strconv.Atoi(fields[3])
			case "Persistence":
				raidInfo.Persistence = strings.Join(fields[2:], " ")
			case "Update":
				updateTimeStr := strings.Join(fields[3:], " ")
				updateTime, _ := time.Parse(time.ANSIC, updateTimeStr)
				raidInfo.UpdateTime = updateTime
			case "State":
				raidInfo.State = strings.Join(fields[2:], " ")
			case "Active":
				raidInfo.ActiveDevices, _ = strconv.Atoi(fields[3])
			case "Working":
				raidInfo.WorkingDevices, _ = strconv.Atoi(fields[3])
			case "Failed":
				raidInfo.FailedDevices, _ = strconv.Atoi(fields[3])
			case "Spare":
				raidInfo.SpareDevices, _ = strconv.Atoi(fields[3])
			case "Consistency":
				raidInfo.Consistency = strings.Join(fields[3:], " ")
			case "Rebuild":
				raidInfo.RebuildStatus = strings.Join(fields[3:], " ")
			case "Name":
				raidInfo.Name = strings.Join(fields[2:], " ")
			case "UUID":
				raidInfo.UUID = fields[2]
			case "Events":
				raidInfo.Events, _ = strconv.Atoi(fields[2])
			default:
				if len(fields) >= 5 && fields[0] != "Number" {
					deviceInfo := DeviceInfo{}

					if len(fields) > 3 {
						rdNo, err := strconv.Atoi(fields[3])
						if err != nil {
							rdNo = -1
						}
						deviceInfo.RaidDevice = rdNo

					}

					if len(fields) > 5 {
						//Only active disks have fields > 5, e.g.
						// 0       8       16        0      active sync   /dev/sdb
						deviceInfo.State = fields[4 : len(fields)-1]
						deviceInfo.DevicePath = fields[len(fields)-1]
					} else {
						//Failed disk, e.g.
						//  -       0        0        1      removed

						deviceInfo.State = fields[4:]
						//TODO: Add custom tags
					}

					raidInfo.DeviceInfo = append(raidInfo.DeviceInfo, deviceInfo)
				}
			}
		}
	}

	return raidInfo
}

// PrettyPrintRAIDInfo pretty prints the RAIDInfo struct.
func (info *RAIDInfo) PrettyPrintRAIDInfo() {
	fmt.Println("RAID Array Information:")
	fmt.Printf("  Version: %s\n", info.Version)
	fmt.Printf("  Creation Time: %s\n", info.CreationTime.Format("Mon Jan 02 15:04:05 2006"))
	fmt.Printf("  Raid Level: %s\n", info.RaidLevel)
	fmt.Printf("  Array Size: %d\n", info.ArraySize)
	fmt.Printf("  Used Dev Size: %d\n", info.UsedDevSize)
	fmt.Printf("  Raid Devices: %d\n", info.RaidDevices)
	fmt.Printf("  Total Devices: %d\n", info.TotalDevices)
	fmt.Printf("  Persistence: %s\n", info.Persistence)
	fmt.Printf("  Update Time: %s\n", info.UpdateTime.Format("Mon Jan 02 15:04:05 2006"))
	fmt.Printf("  State: %s\n", info.State)
	fmt.Printf("  Active Devices: %d\n", info.ActiveDevices)
	fmt.Printf("  Working Devices: %d\n", info.WorkingDevices)
	fmt.Printf("  Failed Devices: %d\n", info.FailedDevices)
	fmt.Printf("  Spare Devices: %d\n", info.SpareDevices)
	fmt.Printf("  Consistency Policy: %s\n", info.Consistency)
	fmt.Printf("  Name: %s\n", info.Name)
	fmt.Printf("  UUID: %s\n", info.UUID)
	fmt.Printf("  Events: %d\n", info.Events)

	fmt.Println("\nDevice Information:")
	fmt.Printf("%s %s\n", "State", "DevicePath")
	for _, device := range info.DeviceInfo {
		fmt.Printf("%s %s\n", strings.Join(device.State, ","), device.DevicePath)
	}
}