package services

import (
	"path/filepath"
	"testing"
	"time"

	"git.mchus.pro/mchus/quoteforge/internal/localdb"
	syncsvc "git.mchus.pro/mchus/quoteforge/internal/services/sync"
)

func TestParseCFXMLWorkspaceGroupsSoftwareIntoConfiguration(t *testing.T) {
	const sample = `<?xml version="1.0" encoding="UTF-8"?>
<CFXML>
  <thisDocumentIdentifier>
    <ProprietaryDocumentIdentifier>CFXML.workspace-test</ProprietaryDocumentIdentifier>
  </thisDocumentIdentifier>
  <CFData>
    <ProprietaryInformation>
      <Name>Currencies</Name>
      <Value>USD</Value>
    </ProprietaryInformation>
    <ProductLineItem>
      <ProductLineNumber>1000</ProductLineNumber>
      <ItemNo>1000</ItemNo>
      <TransactionType>NEW</TransactionType>
      <ProprietaryGroupIdentifier>1000</ProprietaryGroupIdentifier>
      <ConfigurationGroupLineNumberReference>100</ConfigurationGroupLineNumberReference>
      <Quantity>6</Quantity>
      <ProductIdentification>
        <PartnerProductIdentification>
          <ProprietaryProductIdentifier>7DG9-CTO1WW</ProprietaryProductIdentifier>
          <ProductDescription>ThinkSystem SR630 V4</ProductDescription>
          <ProductName>#1</ProductName>
          <ProductTypeCode>Hardware</ProductTypeCode>
        </PartnerProductIdentification>
      </ProductIdentification>
      <UnitListPrice>
        <FinancialAmount>
          <GlobalCurrencyCode>USD</GlobalCurrencyCode>
          <MonetaryAmount>100.00</MonetaryAmount>
        </FinancialAmount>
      </UnitListPrice>
      <ProductSubLineItem>
        <LineNumber>1001</LineNumber>
        <TransactionType>ADD</TransactionType>
        <Quantity>2</Quantity>
        <ProductIdentification>
          <PartnerProductIdentification>
            <ProprietaryProductIdentifier>CPU-1</ProprietaryProductIdentifier>
            <ProductDescription>CPU</ProductDescription>
            <ProductCharacter>PROCESSOR</ProductCharacter>
          </PartnerProductIdentification>
        </ProductIdentification>
        <UnitListPrice>
          <FinancialAmount>
            <GlobalCurrencyCode>USD</GlobalCurrencyCode>
            <MonetaryAmount>0</MonetaryAmount>
          </FinancialAmount>
        </UnitListPrice>
      </ProductSubLineItem>
    </ProductLineItem>
    <ProductLineItem>
      <ProductLineNumber>2000</ProductLineNumber>
      <ItemNo>2000</ItemNo>
      <TransactionType>NEW</TransactionType>
      <ProprietaryGroupIdentifier>1000</ProprietaryGroupIdentifier>
      <ConfigurationGroupLineNumberReference>100</ConfigurationGroupLineNumberReference>
      <Quantity>6</Quantity>
      <ProductIdentification>
        <PartnerProductIdentification>
          <ProprietaryProductIdentifier>7S0X-CTO8WW</ProprietaryProductIdentifier>
          <ProductDescription>XClarity Controller Prem-FOD</ProductDescription>
          <ProductName>software1</ProductName>
          <ProductTypeCode>Software</ProductTypeCode>
        </PartnerProductIdentification>
      </ProductIdentification>
      <UnitListPrice>
        <FinancialAmount>
          <GlobalCurrencyCode>USD</GlobalCurrencyCode>
          <MonetaryAmount>25.00</MonetaryAmount>
        </FinancialAmount>
      </UnitListPrice>
      <ProductSubLineItem>
        <LineNumber>2001</LineNumber>
        <TransactionType>ADD</TransactionType>
        <Quantity>1</Quantity>
        <ProductIdentification>
          <PartnerProductIdentification>
            <ProprietaryProductIdentifier>LIC-1</ProprietaryProductIdentifier>
            <ProductDescription>License</ProductDescription>
            <ProductCharacter>SOFTWARE</ProductCharacter>
          </PartnerProductIdentification>
        </ProductIdentification>
        <UnitListPrice>
          <FinancialAmount>
            <GlobalCurrencyCode>USD</GlobalCurrencyCode>
            <MonetaryAmount>0</MonetaryAmount>
          </FinancialAmount>
        </UnitListPrice>
      </ProductSubLineItem>
    </ProductLineItem>
    <ProductLineItem>
      <ProductLineNumber>3000</ProductLineNumber>
      <ItemNo>3000</ItemNo>
      <TransactionType>NEW</TransactionType>
      <ProprietaryGroupIdentifier>3000</ProprietaryGroupIdentifier>
      <ConfigurationGroupLineNumberReference>100</ConfigurationGroupLineNumberReference>
      <Quantity>2</Quantity>
      <ProductIdentification>
        <PartnerProductIdentification>
          <ProprietaryProductIdentifier>7DG9-CTO1WW</ProprietaryProductIdentifier>
          <ProductDescription>ThinkSystem SR630 V4</ProductDescription>
          <ProductName>#2</ProductName>
          <ProductTypeCode>Hardware</ProductTypeCode>
        </PartnerProductIdentification>
      </ProductIdentification>
      <UnitListPrice>
        <FinancialAmount>
          <GlobalCurrencyCode>USD</GlobalCurrencyCode>
          <MonetaryAmount>90.00</MonetaryAmount>
        </FinancialAmount>
      </UnitListPrice>
    </ProductLineItem>
  </CFData>
</CFXML>`

	workspace, err := parseCFXMLWorkspace([]byte(sample), "sample.xml")
	if err != nil {
		t.Fatalf("parseCFXMLWorkspace: %v", err)
	}

	if workspace.SourceFormat != "CFXML" {
		t.Fatalf("unexpected source format: %q", workspace.SourceFormat)
	}
	if len(workspace.Configurations) != 2 {
		t.Fatalf("expected 2 configurations, got %d", len(workspace.Configurations))
	}

	first := workspace.Configurations[0]
	if first.GroupID != "1000" {
		t.Fatalf("expected first group 1000, got %q", first.GroupID)
	}
	if first.Name != "#1" {
		t.Fatalf("expected first config name #1, got %q", first.Name)
	}
	if first.ServerCount != 6 {
		t.Fatalf("expected first server count 6, got %d", first.ServerCount)
	}
	if len(first.Rows) != 4 {
		t.Fatalf("expected 4 vendor rows in first config, got %d", len(first.Rows))
	}

	foundSoftwareTopLevel := false
	foundSoftwareSubRow := false
	foundPrimaryTopLevelQty := 0
	for _, row := range first.Rows {
		if row.VendorPartnumber == "7DG9-CTO1WW" {
			foundPrimaryTopLevelQty = row.Quantity
		}
		if row.VendorPartnumber == "7S0X-CTO8WW" {
			foundSoftwareTopLevel = true
		}
		if row.VendorPartnumber == "LIC-1" {
			foundSoftwareSubRow = true
		}
	}
	if !foundSoftwareTopLevel {
		t.Fatalf("expected software top-level row to stay inside configuration")
	}
	if !foundSoftwareSubRow {
		t.Fatalf("expected software sub-row to stay inside configuration")
	}
	if foundPrimaryTopLevelQty != 1 {
		t.Fatalf("expected primary top-level qty normalized to 1, got %d", foundPrimaryTopLevelQty)
	}
	if first.TotalPrice == nil || *first.TotalPrice != 750 {
		t.Fatalf("expected first total price 750, got %+v", first.TotalPrice)
	}

	second := workspace.Configurations[1]
	if second.Name != "#2" {
		t.Fatalf("expected second config name #2, got %q", second.Name)
	}
	if len(second.Rows) != 1 {
		t.Fatalf("expected second config to contain single top-level row, got %d", len(second.Rows))
	}
}

func TestImportVendorWorkspaceToProject_AutoResolvesAndAppliesEstimate(t *testing.T) {
	local, err := localdb.New(filepath.Join(t.TempDir(), "local.db"))
	if err != nil {
		t.Fatalf("init local db: %v", err)
	}
	t.Cleanup(func() { _ = local.Close() })

	projectName := "OPS-2079"
	if err := local.SaveProject(&localdb.LocalProject{
		UUID:          "project-1",
		OwnerUsername: "tester",
		Code:          "OPS-2079",
		Variant:       "",
		Name:          &projectName,
		IsActive:      true,
		CreatedAt:     time.Now(),
		UpdatedAt:     time.Now(),
	}); err != nil {
		t.Fatalf("save project: %v", err)
	}

	if err := local.SaveLocalPricelist(&localdb.LocalPricelist{
		ServerID:  101,
		Source:    "estimate",
		Version:   "E-1",
		Name:      "Estimate",
		CreatedAt: time.Now(),
		SyncedAt:  time.Now(),
	}); err != nil {
		t.Fatalf("save estimate pricelist: %v", err)
	}
	estimatePL, err := local.GetLocalPricelistByServerID(101)
	if err != nil {
		t.Fatalf("get estimate pricelist: %v", err)
	}
	if err := local.SaveLocalPricelistItems([]localdb.LocalPricelistItem{
		{PricelistID: estimatePL.ID, LotName: "CPU_INTEL_6747P", Price: 1000},
		{PricelistID: estimatePL.ID, LotName: "LICENSE_XCC", Price: 50},
	}); err != nil {
		t.Fatalf("save estimate items: %v", err)
	}

	bookRepo := local.DB()
	if err := bookRepo.Create(&localdb.LocalPartnumberBook{
		ServerID:        501,
		Version:         "B-1",
		CreatedAt:       time.Now(),
		IsActive:        true,
		PartnumbersJSON: localdb.LocalStringList{"CPU-1", "LIC-1"},
	}).Error; err != nil {
		t.Fatalf("save active book: %v", err)
	}
	if err := bookRepo.Create([]localdb.LocalPartnumberBookItem{
		{Partnumber: "CPU-1", LotsJSON: localdb.LocalPartnumberBookLots{{LotName: "CPU_INTEL_6747P", Qty: 1}}},
		{Partnumber: "LIC-1", LotsJSON: localdb.LocalPartnumberBookLots{{LotName: "LICENSE_XCC", Qty: 1}}},
	}).Error; err != nil {
		t.Fatalf("save book items: %v", err)
	}

	service := NewLocalConfigurationService(local, syncsvc.NewService(nil, local), &QuoteService{}, func() bool { return false })

	const sample = `<?xml version="1.0" encoding="UTF-8"?>
<CFXML>
  <thisDocumentIdentifier>
    <ProprietaryDocumentIdentifier>CFXML.workspace-test</ProprietaryDocumentIdentifier>
  </thisDocumentIdentifier>
  <CFData>
    <ProductLineItem>
      <ProductLineNumber>1000</ProductLineNumber>
      <ItemNo>1000</ItemNo>
      <TransactionType>NEW</TransactionType>
      <ProprietaryGroupIdentifier>1000</ProprietaryGroupIdentifier>
      <Quantity>2</Quantity>
      <ProductIdentification>
        <PartnerProductIdentification>
          <ProprietaryProductIdentifier>7DG9-CTO1WW</ProprietaryProductIdentifier>
          <ProductDescription>ThinkSystem SR630 V4</ProductDescription>
          <ProductName>#1</ProductName>
          <ProductTypeCode>Hardware</ProductTypeCode>
        </PartnerProductIdentification>
      </ProductIdentification>
      <ProductSubLineItem>
        <LineNumber>1001</LineNumber>
        <Quantity>2</Quantity>
        <ProductIdentification>
          <PartnerProductIdentification>
            <ProprietaryProductIdentifier>CPU-1</ProprietaryProductIdentifier>
            <ProductDescription>CPU</ProductDescription>
          </PartnerProductIdentification>
        </ProductIdentification>
      </ProductSubLineItem>
    </ProductLineItem>
    <ProductLineItem>
      <ProductLineNumber>2000</ProductLineNumber>
      <ItemNo>2000</ItemNo>
      <TransactionType>NEW</TransactionType>
      <ProprietaryGroupIdentifier>1000</ProprietaryGroupIdentifier>
      <Quantity>2</Quantity>
      <ProductIdentification>
        <PartnerProductIdentification>
          <ProprietaryProductIdentifier>7S0X-CTO8WW</ProprietaryProductIdentifier>
          <ProductDescription>XClarity Controller</ProductDescription>
          <ProductName>software1</ProductName>
          <ProductTypeCode>Software</ProductTypeCode>
        </PartnerProductIdentification>
      </ProductIdentification>
      <ProductSubLineItem>
        <LineNumber>2001</LineNumber>
        <Quantity>1</Quantity>
        <ProductIdentification>
          <PartnerProductIdentification>
            <ProprietaryProductIdentifier>LIC-1</ProprietaryProductIdentifier>
            <ProductDescription>License</ProductDescription>
          </PartnerProductIdentification>
        </ProductIdentification>
      </ProductSubLineItem>
    </ProductLineItem>
  </CFData>
</CFXML>`

	result, err := service.ImportVendorWorkspaceToProject("project-1", "sample.xml", []byte(sample), "tester")
	if err != nil {
		t.Fatalf("ImportVendorWorkspaceToProject: %v", err)
	}
	if result.Imported != 1 || len(result.Configs) != 1 {
		t.Fatalf("unexpected import result: %+v", result)
	}

	cfg, err := local.GetConfigurationByUUID(result.Configs[0].UUID)
	if err != nil {
		t.Fatalf("load imported config: %v", err)
	}
	if cfg.PricelistID == nil || *cfg.PricelistID != 101 {
		t.Fatalf("expected estimate pricelist id 101, got %+v", cfg.PricelistID)
	}
	if len(cfg.VendorSpec) != 4 {
		t.Fatalf("expected 4 vendor spec rows, got %d", len(cfg.VendorSpec))
	}
	if len(cfg.Items) != 2 {
		t.Fatalf("expected 2 cart items, got %d", len(cfg.Items))
	}
	if cfg.Items[0].LotName != "CPU_INTEL_6747P" || cfg.Items[0].Quantity != 2 || cfg.Items[0].UnitPrice != 1000 {
		t.Fatalf("unexpected first item: %+v", cfg.Items[0])
	}
	if cfg.Items[1].LotName != "LICENSE_XCC" || cfg.Items[1].Quantity != 1 || cfg.Items[1].UnitPrice != 50 {
		t.Fatalf("unexpected second item: %+v", cfg.Items[1])
	}
	if cfg.TotalPrice == nil || *cfg.TotalPrice != 4100 {
		t.Fatalf("expected total price 4100 for 2 servers, got %+v", cfg.TotalPrice)
	}

	foundCPU := false
	foundLIC := false
	for _, row := range cfg.VendorSpec {
		if row.VendorPartnumber == "CPU-1" {
			foundCPU = true
			if len(row.LotMappings) != 1 || row.LotMappings[0].LotName != "CPU_INTEL_6747P" || row.LotMappings[0].QuantityPerPN != 1 {
				t.Fatalf("unexpected CPU mappings: %+v", row.LotMappings)
			}
		}
		if row.VendorPartnumber == "LIC-1" {
			foundLIC = true
			if len(row.LotMappings) != 1 || row.LotMappings[0].LotName != "LICENSE_XCC" || row.LotMappings[0].QuantityPerPN != 1 {
				t.Fatalf("unexpected LIC mappings: %+v", row.LotMappings)
			}
		}
	}
	if !foundCPU || !foundLIC {
		t.Fatalf("expected resolved rows for CPU and LIC in vendor spec")
	}
}
