package usecase

import (
	"context"
	"fmt"
	"strconv"
	"strings"
	"time"

	"regexp"

	"github.com/Cepat-Kilat-Teknologi/go-snmp-olt-zte-c320/internal/model"
	"github.com/Cepat-Kilat-Teknologi/go-snmp-olt-zte-c320/pkg/telnet"
	"github.com/rs/zerolog/log"
)

func (u *onuUsecase) connectTelnet(ctx context.Context, oltID string) (*telnet.ZTETelnetClient, error) {
	olt, err := u.oltRepository.GetByID(ctx, oltID)
	if err != nil {
		return nil, fmt.Errorf("failed to get OLT info: %v", err)
	}

	user := olt.TelnetUsername
	if user == "" {
		user = "zte"
	}
	pass := olt.TelnetPassword
	if pass == "" {
		pass = "zte"
	}

	telnetIP := olt.TelnetIP
	if telnetIP == "" {
		telnetIP = olt.IP
	}

	telnetPort := olt.TelnetPort
	if telnetPort == 0 {
		telnetPort = 23
	}

	client := telnet.NewZTETelnetClient(telnetIP, telnetPort, user, pass)
	if err := client.Connect(); err != nil {
		return nil, fmt.Errorf("telnet connection failed: %v", err)
	}
	return client, nil
}

func (u *onuUsecase) GetDetail(ctx context.Context, oltID string, boardID, ponID, onuID int) (string, error) {
	client, err := u.connectTelnet(ctx, oltID)
	if err != nil {
		return "", err
	}
	defer client.Close()

	interfaceID := fmt.Sprintf("gpon-onu_1/%d/%d:%d", boardID, ponID, onuID)
	cmd := fmt.Sprintf("show gpon onu detail-info %s", interfaceID)

	log.Info().Str("olt", oltID).Str("cmd", cmd).Msg("Executing Telnet Command: GetStatus")

	output, err := client.RunCommand(cmd)
	if err != nil {
		return "", fmt.Errorf("command execution failed: %v", err)
	}

	return output, nil
}

func (u *onuUsecase) GetRunningConfig(ctx context.Context, oltID string, boardID, ponID, onuID int) (string, error) {
	client, err := u.connectTelnet(ctx, oltID)
	if err != nil {
		return "", err
	}
	defer client.Close()

	interfaceID := fmt.Sprintf("gpon-onu_1/%d/%d:%d", boardID, ponID, onuID)

	// Command 1: show running-config interface ...
	cmd1 := fmt.Sprintf("show running-config interface %s", interfaceID)
	log.Info().Str("olt", oltID).Str("cmd", cmd1).Msg("Executing Telnet Command: Config 1")
	out1, err := client.RunCommand(cmd1)
	if err != nil {
		return "", fmt.Errorf("command 1 execution failed: %v", err)
	}

	// Command 2: show onu running config ...
	cmd2 := fmt.Sprintf("show onu running config %s", interfaceID)
	log.Info().Str("olt", oltID).Str("cmd", cmd2).Msg("Executing Telnet Command: Config 2")
	out2, err := client.RunCommand(cmd2)
	if err != nil {
		return "", fmt.Errorf("command 2 execution failed: %v", err)
	}

	finalOutput := fmt.Sprintf("--- %s ---\n%s\n\n--- %s ---\n%s", cmd1, out1, cmd2, out2)
	return finalOutput, nil
}

func (u *onuUsecase) GetMacTable(ctx context.Context, oltID string, boardID, ponID, onuID int) (string, error) {
	client, err := u.connectTelnet(ctx, oltID)
	if err != nil {
		return "", err
	}
	defer client.Close()

	interfaceID := fmt.Sprintf("gpon-onu_1/%d/%d:%d", boardID, ponID, onuID)
	cmd := fmt.Sprintf("show mac gpon onu %s", interfaceID)
	log.Info().Str("olt", oltID).Str("cmd", cmd).Msg("Executing Telnet Command: MAC Table")

	output, err := client.RunCommand(cmd)
	if err != nil {
		return "", fmt.Errorf("command execution failed: %v", err)
	}
	return output, nil
}

func (u *onuUsecase) GetOpticalPower(ctx context.Context, oltID string, boardID, ponID, onuID int) (string, error) {
	client, err := u.connectTelnet(ctx, oltID)
	if err != nil {
		return "", err
	}
	defer client.Close()

	interfaceID := fmt.Sprintf("gpon-onu_1/%d/%d:%d", boardID, ponID, onuID)

	// Command 1: show pon power onu-rx ...
	cmd1 := fmt.Sprintf("show pon power onu-rx %s", interfaceID)
	log.Info().Str("olt", oltID).Str("cmd", cmd1).Msg("Executing Telnet Command: Power ONU-RX")
	out1, err := client.RunCommand(cmd1)
	if err != nil {
		return "", fmt.Errorf("command 1 execution failed: %v", err)
	}

	// Command 2: show pon power olt-rx ...
	cmd2 := fmt.Sprintf("show pon power olt-rx %s", interfaceID)
	log.Info().Str("olt", oltID).Str("cmd", cmd2).Msg("Executing Telnet Command: Power OLT-RX")
	out2, err := client.RunCommand(cmd2)
	if err != nil {
		return "", fmt.Errorf("command 2 execution failed: %v", err)
	}

	finalOutput := fmt.Sprintf("--- %s ---\n%s\n\n--- %s ---\n%s", cmd1, out1, cmd2, out2)
	return finalOutput, nil
}

func (u *onuUsecase) GetIPHost(ctx context.Context, oltID string, boardID, ponID, onuID int) (string, error) {
	client, err := u.connectTelnet(ctx, oltID)
	if err != nil {
		return "", err
	}
	defer client.Close()

	interfaceID := fmt.Sprintf("gpon-onu_1/%d/%d:%d", boardID, ponID, onuID)
	cmd := fmt.Sprintf("show gpon remote ip-host %s", interfaceID)
	log.Info().Str("olt", oltID).Str("cmd", cmd).Msg("Executing Telnet Command: Check IP")

	output, err := client.RunCommand(cmd)
	if err != nil {
		return "", fmt.Errorf("command execution failed: %v", err)
	}
	return output, nil
}

func (u *onuUsecase) GetEthPort(ctx context.Context, oltID string, boardID, ponID, onuID int) (string, error) {
	client, err := u.connectTelnet(ctx, oltID)
	if err != nil {
		return "", err
	}
	defer client.Close()

	interfaceID := fmt.Sprintf("gpon-onu_1/%d/%d:%d", boardID, ponID, onuID)
	cmd := fmt.Sprintf("show gpon remote-onu interface eth %s", interfaceID)
	log.Info().Str("olt", oltID).Str("cmd", cmd).Msg("Executing Telnet Command: Check Eth")

	output, err := client.RunCommand(cmd)
	if err != nil {
		return "", fmt.Errorf("command execution failed: %v", err)
	}
	return output, nil
}

func (u *onuUsecase) RegisterONU(ctx context.Context, req model.RegisterOnuRequest) (string, error) {
	olt, err := u.oltRepository.GetByID(ctx, req.OltID)
	if err != nil {
		return "", fmt.Errorf("failed to get OLT info for ACS: %v", err)
	}

	client, err := u.connectTelnet(ctx, req.OltID)
	if err != nil {
		return "", err
	}
	defer client.Close()

	// Defaults if not provided
	vlan := req.Vlan
	if vlan == 0 {
		vlan = olt.InternetVlan
		if vlan == 0 {
			vlan = 2100 // Hard fallback
		}
	}

	tr069Vlan := olt.Tr069Vlan
	if tr069Vlan == 0 {
		tr069Vlan = 100 // Default TR069 VLAN
	}

	pppoeUser := req.PPPOEUser
	if pppoeUser == "" {
		pppoeUser = req.Name
	}

	interfaceID := fmt.Sprintf("1/%d/%d:%d", req.Board, req.PON, req.OnuID)
	oltInterface := fmt.Sprintf("gpon-olt_1/%d/%d", req.Board, req.PON)
	onuInterface := fmt.Sprintf("gpon-onu_%s", interfaceID)
	mngInterface := fmt.Sprintf("gpon-onu_%s", interfaceID)

	vlanIptv := req.IptvVlan
	if vlanIptv == 0 {
		vlanIptv = olt.IptvVlan
		if vlanIptv == 0 {
			vlanIptv = 778
		}
	}

	var sb strings.Builder

	// 1. Enter config terminal
	sb.WriteString("configure terminal\n")

	// 2. Register ONU
	sb.WriteString(fmt.Sprintf("interface %s\n", oltInterface))
	regCmd := fmt.Sprintf("  onu %d type %s sn %s", req.OnuID, req.OnuType, req.SN)
	if req.Password != "" && req.Password != "N/A" {
		regCmd += fmt.Sprintf(" password %s", req.Password)
	}
	sb.WriteString(regCmd + "\n")
	sb.WriteString("!\n")

	// 3. Configure ONU Interface
	sb.WriteString(fmt.Sprintf("interface %s\n", onuInterface))
	sb.WriteString(fmt.Sprintf("  name %s\n", req.Name))
	desc := req.Description
	if desc == "" {
		desc = "none"
	}
	sb.WriteString(fmt.Sprintf("  description \"%s\"\n", desc))
	sb.WriteString("  tcont 1 profile SMARTOLT-1G-UP\n")

	if req.EnableTr069 {
		sb.WriteString("  tcont 2 profile SMARTOLT-VOIPMNG-10M\n")
	}

	if req.EnableIptv {
		sb.WriteString("  tcont 3 profile SMARTOLT-IPTV-10M-UP\n")
	}

	sb.WriteString("  gemport 1 tcont 1\n")
	sb.WriteString("  gemport 1 traffic-limit downstream SMARTOLT-1G-DOWN\n")

	if req.EnableTr069 {
		sb.WriteString("  gemport 2 tcont 2\n")
		sb.WriteString("  gemport 2 traffic-limit downstream SMARTOLT-VOIPMNG-10M\n")
	}

	if req.EnableIptv {
		sb.WriteString("  gemport 3 tcont 3\n")
		sb.WriteString("  gemport 3 traffic-limit downstream SMARTOLT-IPTV-50M-DOWN\n")
	}

	sb.WriteString(fmt.Sprintf("  service-port 1 vport 1 user-vlan %d vlan %d\n", vlan, vlan))

	if req.EnableTr069 {
		sb.WriteString(fmt.Sprintf("  service-port 2 vport 2 user-vlan %d vlan %d\n", tr069Vlan, tr069Vlan))
	}

	if req.EnableIptv {
		sb.WriteString(fmt.Sprintf("  service-port 3 vport 3 user-vlan %d vlan %d\n", vlanIptv, vlanIptv))
		sb.WriteString("  ip dhcp snooping enable vport 3\n")
	}

	sb.WriteString("!\n")

	// 4. Configure ONU Management (pon-onu-mng)
	sb.WriteString(fmt.Sprintf("pon-onu-mng %s\n", mngInterface))

	if req.EnableTr069 {
		sb.WriteString("  voip protocol sip\n")
		sb.WriteString("  flow 2 switch switch_0/1\n")
	}

	if req.EnableIptv {
		sb.WriteString("  flow 3 switch switch_0/1\n")
	}

	sb.WriteString("  flow mode 1 tag-filter vlan-filter untag-filter discard\n")
	if req.EnableTr069 {
		sb.WriteString("  flow mode 2 tag-filter vlan-filter untag-filter discard\n")
	}
	if req.EnableIptv {
		sb.WriteString("  flow mode 3 tag-filter vlan-filter untag-filter discard\n")
	}

	sb.WriteString(fmt.Sprintf("  flow 1 pri 0 vlan %d\n", vlan))
	if req.EnableTr069 {
		sb.WriteString(fmt.Sprintf("  flow 2 pri 2 vlan %d\n", tr069Vlan))
	}

	sb.WriteString("  gemport 1 flow 1\n")
	if req.EnableTr069 {
		sb.WriteString("  gemport 2 flow 2\n")
	}

	if req.EnableIptv {
		sb.WriteString(fmt.Sprintf("  flow 3 pri 5 vlan %d\n", vlanIptv))
		sb.WriteString("  gemport 3 flow 3\n")
		sb.WriteString(fmt.Sprintf("  vlan port eth_0/4 mode tag vlan %d\n", vlanIptv))
		sb.WriteString("  dhcp-ip ethuni eth_0/4 from-internet\n")
		sb.WriteString(fmt.Sprintf("  mvlan %d\n", vlanIptv))
	}

	sb.WriteString("  switchport-bind switch_0/1 iphost 1\n")
	if req.EnableTr069 {
		sb.WriteString("  switchport-bind switch_0/1 iphost 2\n")
	}
	sb.WriteString("  switchport-bind switch_0/1 veip 1\n")
	sb.WriteString(fmt.Sprintf("  pppoe 1 nat enable user %s password %s\n", pppoeUser, req.PPPOEPassword))

	if req.EnableTr069 {
		sb.WriteString("  ip-host 2 dhcp-enable enable ping-response enable traceroute-response enable\n")
	}

	sb.WriteString("  vlan-filter-mode iphost 1 tag-filter vlan-filter untag-filter discard\n")
	if req.EnableTr069 {
		sb.WriteString("  vlan-filter-mode iphost 2 tag-filter vlan-filter untag-filter discard\n")
	}

	sb.WriteString(fmt.Sprintf("  vlan-filter iphost 1 pri 0 vlan %d\n", vlan))
	if req.EnableTr069 {
		sb.WriteString(fmt.Sprintf("  vlan-filter iphost 2 pri 2 vlan %d\n", tr069Vlan))
	}

	sb.WriteString("  dhcp-ip ethuni eth_0/1 from-onu\n")
	sb.WriteString("  dhcp-ip ethuni eth_0/2 from-onu\n")
	sb.WriteString("  dhcp-ip ethuni eth_0/3 from-onu\n")
	if req.EnableIptv {
		sb.WriteString("  dhcp-ip ethuni eth_0/4 from-internet\n")
	} else {
		sb.WriteString("  dhcp-ip ethuni eth_0/4 from-onu\n")
	}
	sb.WriteString("  dhcp-ip ethuni eth_0/5 from-onu\n")
	sb.WriteString("  dhcp-ip ethuni eth_0/6 from-onu\n")
	sb.WriteString("  dhcp-ip ethuni eth_0/7 from-onu\n")
	sb.WriteString("  dhcp-ip ethuni eth_0/8 from-onu\n")

	if req.EnableTr069 {
		sb.WriteString("  veip 1 port udp 1232 host 2\n")
		sb.WriteString("  tr069-mgmt 1 state unlock\n")
		if olt.ACSUrl != "" {
			sb.WriteString(fmt.Sprintf("  tr069-mgmt 1 acs %s validate basic username %s password %s\n", olt.ACSUrl, olt.ACSUsername, olt.ACSPassword))
		}
		sb.WriteString(fmt.Sprintf("  tr069-mgmt 1 tag pri 2 vlan %d\n", tr069Vlan))
	}

	sb.WriteString("  security-mgmt 1 state enable mode forward protocol web https\n")
	sb.WriteString("  security-mgmt 5 state enable mode forward protocol web https\n")
	sb.WriteString("  security-mgmt 998 state enable mode forward ingress-type lan protocol web https\n")
	sb.WriteString("  security-mgmt 999 state enable ingress-type lan protocol ftp telnet ssh snmp tr069\n")
	sb.WriteString("!\n")
	if req.EnableIptv {
		sb.WriteString(fmt.Sprintf("igmp mvlan %d\n", vlanIptv))
		sb.WriteString(fmt.Sprintf("igmp mvlan %d receive-port %s vport 3\n", vlanIptv, onuInterface))
	}
	sb.WriteString("end\n")

	// Execute commands one by one or as a block
	commands := strings.Split(sb.String(), "\n")
	var fullOutput strings.Builder
	for _, c := range commands {
		cmd := strings.TrimSpace(c)
		if cmd == "" && c != "!" { // keep ! for visual if needed but RunCommand handles empty lines usually
			continue
		}
		out, err := client.RunCommand(c)
		fullOutput.WriteString(fmt.Sprintf("CMD: %s\nOUT: %s\n", c, out))
		if err != nil {
			log.Error().Err(err).Str("cmd", c).Msg("Telnet command execution failed")
			// Return partial output for debugging
			return fullOutput.String(), fmt.Errorf("command '%s' failed: %v", c, err)
		}

		// Check for OLT side errors
		if strings.Contains(out, "% Error") || strings.Contains(out, "Invalid input") || strings.Contains(out, "error") {
			log.Error().Str("cmd", c).Str("out", out).Msg("OLT returned an error")
			return fullOutput.String(), fmt.Errorf("OLT error on command '%s': %s", c, strings.TrimSpace(out))
		}
	}

	// After successful registration, query ONU data and save to database
	log.Info().
		Str("olt_id", req.OltID).
		Int("board", req.Board).
		Int("pon", req.PON).
		Int("onu_id", req.OnuID).
		Msg("Registration successful, querying ONU data to save to database")

	// Log full output for debugging (debug level)
	log.Info().Str("telnet_output", fullOutput.String()).Msg("Full Telnet Registration Output")

	// Wait a bit for ONU to come online (2 seconds)
	time.Sleep(2 * time.Second)

	// Get ONU data from SNMP
	onuData, err := u.GetByBoardIDPonIDAndOnuID(ctx, req.OltID, req.Board, req.PON, req.OnuID)
	if err != nil {
		log.Warn().Err(err).
			Str("olt_id", req.OltID).
			Int("board", req.Board).
			Int("pon", req.PON).
			Int("onu_id", req.OnuID).
			Msg("Failed to query ONU data after registration, but registration was successful")
		// Don't return error, registration was successful
	} else {
		// Save to database
		onus := []model.ONUCustomerInfo{onuData}
		err = u.postgresRepository.SaveONUs(ctx, onus)
		if err != nil {
			log.Warn().Err(err).
				Str("olt_id", req.OltID).
				Int("board", req.Board).
				Int("pon", req.PON).
				Int("onu_id", req.OnuID).
				Msg("Failed to save ONU data to database after registration")
		} else {
			log.Info().
				Str("olt_id", req.OltID).
				Int("board", req.Board).
				Int("pon", req.PON).
				Int("onu_id", req.OnuID).
				Str("name", onuData.Name).
				Str("serial_number", onuData.SerialNumber).
				Msg("Successfully saved newly registered ONU to database")
		}
	}

	return fullOutput.String(), nil
}

func (u *onuUsecase) DeleteONU(ctx context.Context, oltID string, boardID, ponID, onuID int) (string, error) {
	client, err := u.connectTelnet(ctx, oltID)
	if err != nil {
		return "", err
	}
	defer client.Close()

	oltInterface := fmt.Sprintf("interface gpon-olt_1/%d/%d", boardID, ponID)

	var sb strings.Builder
	sb.WriteString("configure terminal\n")
	sb.WriteString(fmt.Sprintf("%s\n", oltInterface))
	sb.WriteString(fmt.Sprintf("  no onu %d\n", onuID))
	sb.WriteString("!\n")
	sb.WriteString("end\n")

	commands := strings.Split(sb.String(), "\n")
	var fullOutput strings.Builder
	for _, c := range commands {
		if strings.TrimSpace(c) == "" && c != "!" {
			continue
		}
		out, err := client.RunCommand(c)
		fullOutput.WriteString(out + "\n")
		if err != nil {
			return fullOutput.String(), fmt.Errorf("command '%s' failed: %v", c, err)
		}
	}

	// Delete ONU data from database after successful unbind
	err = u.postgresRepository.DeleteByONU(ctx, oltID, boardID, ponID, onuID)
	if err != nil {
		log.Warn().Err(err).
			Str("olt_id", oltID).
			Int("board", boardID).
			Int("pon", ponID).
			Int("onu_id", onuID).
			Msg("Failed to delete ONU from database, but unbind was successful")
		// Don't return error here, unbind was successful
	} else {
		log.Info().
			Str("olt_id", oltID).
			Int("board", boardID).
			Int("pon", ponID).
			Int("onu_id", onuID).
			Msg("Successfully deleted ONU from database")
	}

	return fullOutput.String(), nil
}

func (u *onuUsecase) RebootONU(ctx context.Context, oltID string, boardID, ponID, onuID int) (string, error) {
	client, err := u.connectTelnet(ctx, oltID)
	if err != nil {
		return "", err
	}
	defer client.Close()

	interfaceID := fmt.Sprintf("gpon-onu_1/%d/%d:%d", boardID, ponID, onuID)

	var fullOutput strings.Builder

	out1, _ := client.RunCommand("configure terminal")
	fullOutput.WriteString(out1 + "\n")

	out2, _ := client.RunCommand(fmt.Sprintf("pon-onu-mng %s", interfaceID))
	fullOutput.WriteString(out2 + "\n")

	// Send reboot command
	out3, _ := client.RunCommand("reboot")
	fullOutput.WriteString(out3 + "\n")

	// Confirm reboot with "yes"
	out4, err := client.RunCommand("yes")
	fullOutput.WriteString(out4 + "\n")
	if err != nil {
		return fullOutput.String(), err
	}

	out5, _ := client.RunCommand("end")
	fullOutput.WriteString(out5 + "\n")

	return fullOutput.String(), nil
}

func (u *onuUsecase) RestoreFactoryONU(ctx context.Context, oltID string, boardID, ponID, onuID int) (string, error) {
	client, err := u.connectTelnet(ctx, oltID)
	if err != nil {
		return "", err
	}
	defer client.Close()

	interfaceID := fmt.Sprintf("gpon-onu_1/%d/%d:%d", boardID, ponID, onuID)

	_, _ = client.RunCommand("configure terminal")
	_, _ = client.RunCommand(fmt.Sprintf("pon-onu-mng %s", interfaceID))
	out, err := client.RunCommand("restore factory")
	if err != nil {
		return out, err
	}
	_, _ = client.RunCommand("end")
	return out, nil
}

func (u *onuUsecase) ReplaceONU(ctx context.Context, oltID string, boardID, ponID, onuID int, newSN string) (string, error) {
	client, err := u.connectTelnet(ctx, oltID)
	if err != nil {
		return "", err
	}
	defer client.Close()

	interfaceID := fmt.Sprintf("gpon-onu_1/%d/%d:%d", boardID, ponID, onuID)

	_, _ = client.RunCommand("configure terminal")
	_, _ = client.RunCommand(fmt.Sprintf("interface %s", interfaceID))
	out, err := client.RunCommand(fmt.Sprintf("re sn %s", newSN))
	if err != nil {
		return out, err
	}
	_, _ = client.RunCommand("end")
	return out, nil
}

func (u *onuUsecase) GetUnconfiguredONUsTelnet(ctx context.Context, oltID string) ([]model.UnconfiguredOnu, error) {
	client, err := u.connectTelnet(ctx, oltID)
	if err != nil {
		return nil, err
	}
	defer client.Close()

	// Use "show pon onu uncfg" as suggested by the user for C320
	cmd := "show pon onu uncfg"
	log.Info().Str("olt", oltID).Str("cmd", cmd).Msg("Executing Telnet Command: GetUnconfigured (uncfg)")
	output, err := client.RunCommand(cmd)
	if err != nil {
		return nil, fmt.Errorf("failed to run telnet command: %v", err)
	}

	lines := strings.Split(output, "\n")
	var results []model.UnconfiguredOnu

	for _, line := range lines {
		line = strings.TrimSpace(line)
		// Skip headers, empty lines, and separators
		lowLine := strings.ToLower(line)
		if line == "" || strings.HasPrefix(lowLine, "oltindex") || strings.HasPrefix(line, "---") || strings.HasPrefix(lowLine, "show ") || strings.HasPrefix(line, "ZXAN") {
			continue
		}

		fields := strings.Fields(line)
		// Expecting at least OltIndex, Model, SN. PW is optional.
		// Format A (4+ cols): gpon-olt_1/2/1  ModelName  SN  PW
		// Format B (3 cols):  gpon-olt_1/2/1  ModelName  SN
		if len(fields) < 3 {
			continue
		}

		fullIdx := fields[0]
		var sn, pw, modelName string

		// Check if the last field looks like a password (short, often hex) or SN (ZTEG..., HWTC...)
		// Actually, rely on column count heuristic first

		if len(fields) == 3 {
			// Format B: Index, Model, SN
			sn = fields[len(fields)-1]
			pw = "" // No password
			modelName = strings.Join(fields[1:len(fields)-1], " ")
		} else {
			// Format A: Index, Model, SN, PW (standard assumption for >= 4 fields)
			// But careful with model names containing spaces.
			// Usually PW is the very last field.
			pw = fields[len(fields)-1]
			sn = fields[len(fields)-2]
			modelName = strings.Join(fields[1:len(fields)-2], " ")
		}

		if !strings.Contains(fullIdx, "gpon-olt_") {
			continue
		}

		// Parse "gpon-olt_1/2/1"
		idxParts := strings.Split(fullIdx, "_")
		if len(idxParts) < 2 {
			continue
		}

		// Take "1/2/1"
		path := idxParts[1]
		bpParts := strings.Split(path, "/")
		if len(bpParts) < 3 {
			continue
		}

		board, _ := strconv.Atoi(bpParts[1])
		pon, _ := strconv.Atoi(bpParts[2])

		// OnuIndex is usually not in "show pon onu uncfg" output for uncfg ONUs,
		// they are identified by SN. We assign 0 or 1.
		onuIndex := 0

		results = append(results, model.UnconfiguredOnu{
			OltID:        oltID,
			Board:        board,
			PON:          pon,
			OnuIndex:     onuIndex,
			SerialNumber: sn,
			Password:     pw,
			Model:        modelName,
		})
	}

	log.Info().Int("count", len(results)).Msg("Successfully parsed unconfigured ONUs from Telnet (uncfg)")
	return results, nil
}

func (u *onuUsecase) MoveONU(ctx context.Context, req model.MoveOnuRequest) (string, error) {
	log.Info().Str("sn", req.SN).Str("new_olt", req.NewOltID).Msg("Move ONU")

	existing, err := u.postgresRepository.GetBySerialNumber(ctx, req.SN)
	if err != nil {
		return "", fmt.Errorf("check existing: %v", err)
	}
	if existing == nil {
		return "", fmt.Errorf("ONU %s not found", req.SN)
	}

	regReq := model.RegisterOnuRequest{
		OltID: req.NewOltID, Board: req.NewBoard, PON: req.NewPON, OnuID: req.NewOnuID,
		SN: req.SN, OnuType: existing.Model, Name: existing.Name,
		PPPOEUser: existing.Name, PPPOEPassword: "password", Vlan: 2100, EnableTr069: false,
	}

	regOut, err := u.RegisterONU(ctx, regReq)
	if err != nil {
		return "", fmt.Errorf("register failed: %v", err)
	}

	delOut, _ := u.DeleteONU(ctx, existing.OltID, existing.Board, existing.PON, existing.ID)

	return fmt.Sprintf("Moved %s from %s/%d/%d/%d to %s/%d/%d/%d\n%s\n%s",
		req.SN, existing.OltID, existing.Board, existing.PON, existing.ID,
		req.NewOltID, req.NewBoard, req.NewPON, req.NewOnuID, regOut, delOut), nil
}
func (u *onuUsecase) GetOnuVlansTelnet(ctx context.Context, oltID string, boardID, ponID, onuID int) (int, int, error) {
	client, err := u.connectTelnet(ctx, oltID)
	if err != nil {
		return 0, 0, err
	}
	defer client.Close()

	interfaceID := fmt.Sprintf("gpon-onu_1/%d/%d:%d", boardID, ponID, onuID)
	cmd := fmt.Sprintf("show running-config interface %s", interfaceID)

	output, err := client.RunCommand(cmd)
	if err != nil {
		return 0, 0, err
	}

	return u.parseVlansFromConfig(output)
}

func (u *onuUsecase) parseVlansFromConfig(output string) (vlan int, iptvVlan int, err error) {
	// Patterns based on User Rules:
	// Old Manual: Port 1 = Internet, Port 2 = IPTV
	// New App: Port 1 = Internet, Port 2 = TR069 (100), Port 3 = IPTV

	internetRegex := regexp.MustCompile(`service-port\s+1\s+vport\s+1\s+user-vlan\s+(\d+)`)
	port2Regex := regexp.MustCompile(`service-port\s+2\s+vport\s+2\s+user-vlan\s+(\d+)`)
	port3Regex := regexp.MustCompile(`service-port\s+3\s+vport\s+3\s+user-vlan\s+(\d+)`)

	lines := strings.Split(output, "\n")
	for _, line := range lines {
		line = strings.TrimSpace(line)

		// Internet is always Port 1
		if matches := internetRegex.FindStringSubmatch(line); len(matches) > 1 {
			v, _ := strconv.Atoi(matches[1])
			vlan = v
		}

		// Service Port 2 logic
		if matches := port2Regex.FindStringSubmatch(line); len(matches) > 1 {
			v, _ := strconv.Atoi(matches[1])
			if v != 100 {
				// If not VLAN 100, this is the old manual IPTV config
				iptvVlan = v
			}
			// If it IS 100, we skip (it's TR069)
		}

		// Service Port 3 logic
		if matches := port3Regex.FindStringSubmatch(line); len(matches) > 1 {
			v, _ := strconv.Atoi(matches[1])
			// If Service Port 3 exists, it's always IPTV in the new config
			iptvVlan = v
		}
	}

	return vlan, iptvVlan, nil
}

func (u *onuUsecase) SyncVlans(ctx context.Context, oltID string, boardID, ponID, onuID int) (int, int, error) {
	vlan, iptvVlan, err := u.GetOnuVlansTelnet(ctx, oltID, boardID, ponID, onuID)
	if err != nil {
		return 0, 0, err
	}

	// Update DB if we got anything
	if vlan > 0 || iptvVlan > 0 {
		onu, err := u.GetByBoardIDPonIDAndOnuID(ctx, oltID, boardID, ponID, onuID)
		if err == nil {
			onu.Vlan = vlan
			onu.IptvVlan = iptvVlan
			_ = u.postgresRepository.SaveONUs(ctx, []model.ONUCustomerInfo{onu})
		}
	}

	return vlan, iptvVlan, nil
}

func (u *onuUsecase) StandardizeConfig(ctx context.Context, oltID string, boardID, ponID, onuID int) (string, error) {
	// 1. Get current VLANs first to preserve them
	vlan, iptvVlan, err := u.GetOnuVlansTelnet(ctx, oltID, boardID, ponID, onuID)
	if err != nil {
		return "", fmt.Errorf("failed to fetch current VLANs: %v", err)
	}

	// Get OLT info for default VLANs if not detected
	olt, err := u.oltRepository.GetByID(ctx, oltID)
	if err != nil {
		return "", fmt.Errorf("failed to get OLT info: %v", err)
	}

	if vlan == 0 {
		vlan = olt.InternetVlan
		if vlan == 0 {
			vlan = 2100
		}
	}

	if iptvVlan == 0 {
		iptvVlan = olt.IptvVlan
		if iptvVlan == 0 {
			iptvVlan = 778
		}
	}

	tr069Vlan := olt.Tr069Vlan
	if tr069Vlan == 0 {
		tr069Vlan = 100
	}

	client, err := u.connectTelnet(ctx, oltID)
	if err != nil {
		return "", err
	}
	defer client.Close()

	interfaceID := fmt.Sprintf("1/%d/%d:%d", boardID, ponID, onuID)
	onuInterface := fmt.Sprintf("gpon-onu_%s", interfaceID)

	var sb strings.Builder
	sb.WriteString("configure terminal\n")
	sb.WriteString(fmt.Sprintf("interface %s\n", onuInterface))

	// Clean up old service ports (we don't know exactly which ones exist, so we try 1, 2, 3)
	sb.WriteString("  no service-port 1\n")
	sb.WriteString("  no service-port 2\n")
	sb.WriteString("  no service-port 3\n")

	// Apply Standard Mapping
	// Port 1: Internet
	sb.WriteString(fmt.Sprintf("  service-port 1 vport 1 user-vlan %d vlan %d\n", vlan, vlan))
	// Port 2: TR-069 (100)
	sb.WriteString(fmt.Sprintf("  service-port 2 vport 2 user-vlan %d vlan %d\n", tr069Vlan, tr069Vlan))
	// Port 3: IPTV
	sb.WriteString(fmt.Sprintf("  service-port 3 vport 3 user-vlan %d vlan %d\n", iptvVlan, iptvVlan))
	sb.WriteString("  ip dhcp snooping enable vport 3\n")

	sb.WriteString("!\n")

	// Standardize pon-onu-mng configuration
	sb.WriteString(fmt.Sprintf("pon-onu-mng %s\n", onuInterface))
	sb.WriteString("  voip protocol sip\n")
	sb.WriteString("  flow 2 switch switch_0/1\n")
	sb.WriteString("  flow 3 switch switch_0/1\n")
	sb.WriteString("  flow mode 1 tag-filter vlan-filter untag-filter discard\n")
	sb.WriteString("  flow mode 2 tag-filter vlan-filter untag-filter discard\n")
	sb.WriteString("  flow mode 3 tag-filter vlan-filter untag-filter discard\n")
	sb.WriteString(fmt.Sprintf("  flow 1 pri 0 vlan %d\n", vlan))
	sb.WriteString(fmt.Sprintf("  flow 2 pri 2 vlan %d\n", tr069Vlan))
	sb.WriteString(fmt.Sprintf("  flow 3 pri 5 vlan %d\n", iptvVlan))
	sb.WriteString("  gemport 1 flow 1\n")
	sb.WriteString("  gemport 2 flow 2\n")
	sb.WriteString("  gemport 3 flow 3\n")
	sb.WriteString(fmt.Sprintf("  vlan port eth_0/4 mode tag vlan %d\n", iptvVlan))
	sb.WriteString("  dhcp-ip ethuni eth_0/4 from-internet\n")
	sb.WriteString(fmt.Sprintf("  mvlan %d\n", iptvVlan))
	sb.WriteString("  switchport-bind switch_0/1 iphost 1\n")
	sb.WriteString("  switchport-bind switch_0/1 iphost 2\n")
	sb.WriteString("  switchport-bind switch_0/1 veip 1\n")
	sb.WriteString("  ip-host 2 dhcp-enable enable ping-response enable traceroute-response enable\n")
	sb.WriteString("  vlan-filter-mode iphost 1 tag-filter vlan-filter untag-filter discard\n")
	sb.WriteString("  vlan-filter-mode iphost 2 tag-filter vlan-filter untag-filter discard\n")
	sb.WriteString(fmt.Sprintf("  vlan-filter iphost 1 pri 0 vlan %d\n", vlan))
	sb.WriteString(fmt.Sprintf("  vlan-filter iphost 2 pri 2 vlan %d\n", tr069Vlan))
	sb.WriteString("!\n")
	sb.WriteString("end\n")
	sb.WriteString("write\n")

	log.Info().Str("olt", oltID).Str("interface", onuInterface).Msg("Standardizing ONU Configuration")

	commands := strings.Split(sb.String(), "\n")
	var finalOutput strings.Builder
	for _, cmd := range commands {
		if cmd == "" {
			continue
		}
		out, _ := client.RunCommand(cmd)
		finalOutput.WriteString(fmt.Sprintf("> %s\n%s\n", cmd, out))
	}

	return finalOutput.String(), nil
}
