package queries

import (
	"bssapp-backend/auth"
	"bssapp-backend/internal/authz"
	"context"
	"database/sql"
	"fmt"
	"strings"
)

func resolvePiyasaWhere(ctx context.Context, pg *sql.DB) (string, error) {
	claims, ok := auth.GetClaimsFromContext(ctx)
	if !ok || claims == nil {
		return "", fmt.Errorf("unauthorized: claims not found")
	}

	if claims.IsAdmin() {
		return "1=1", nil
	}

	codes, err := authz.GetUserPiyasaCodes(pg, int(claims.ID))
	if err != nil {
		return "", fmt.Errorf("piyasa codes load error: %w", err)
	}

	if len(codes) == 0 {
		return "1=0", nil
	}

	return authz.BuildINClause("UPPER(f2.CustomerAtt01)", codes), nil
}

func GetOrderListCloseReady(
	ctx context.Context,
	mssql *sql.DB,
	pg *sql.DB,
	search string,
) (*sql.Rows, error) {

	piyasaWhere, err := resolvePiyasaWhere(ctx, pg)
	if err != nil {
		return nil, err
	}

	baseQuery := fmt.Sprintf(`
SELECT
    CAST(h.OrderHeaderID AS NVARCHAR(50))           AS OrderHeaderID,
    ISNULL(h.OrderNumber, '')                      AS OrderNumber,
    CONVERT(varchar, h.OrderDate, 23)              AS OrderDate,

    ISNULL(h.CurrAccCode, '')                      AS CurrAccCode,
    ISNULL(ca.CurrAccDescription, '')              AS CurrAccDescription,

    ISNULL(mt.AttributeDescription, '')            AS MusteriTemsilcisi,
    ISNULL(py.AttributeDescription, '')            AS Piyasa,

    CONVERT(varchar, h.CreditableConfirmedDate,23) AS CreditableConfirmedDate,
    ISNULL(h.DocCurrencyCode,'TRY')                AS DocCurrencyCode,

    ISNULL(l.TotalAmount,0)                        AS TotalAmount,

    CASE
        WHEN h.DocCurrencyCode = 'USD'
            THEN ISNULL(l.TotalAmount,0)
        WHEN h.DocCurrencyCode = 'TRY'
             AND usd.Rate > 0
            THEN ISNULL(l.TotalAmount,0) / usd.Rate
        WHEN h.DocCurrencyCode IN ('EUR','GBP')
             AND cur.Rate > 0
             AND usd.Rate > 0
            THEN (ISNULL(l.TotalAmount,0) * cur.Rate) / usd.Rate
        ELSE 0
    END                                            AS TotalAmountUSD,

    ISNULL(l.PackedAmount,0)                       AS PackedAmount,

    CASE
        WHEN h.DocCurrencyCode = 'USD'
            THEN ISNULL(l.PackedAmount,0)
        WHEN h.DocCurrencyCode = 'TRY'
             AND usd.Rate > 0
            THEN ISNULL(l.PackedTRY,0) / usd.Rate
        WHEN cur.Rate > 0
             AND usd.Rate > 0
            THEN (ISNULL(l.PackedAmount,0) * cur.Rate) / usd.Rate
        ELSE 0
    END                                            AS PackedUSD,

    CASE
        WHEN ISNULL(l.TotalAmount,0) > 0
            THEN (ISNULL(l.PackedAmount,0) * 100.0) / NULLIF(l.TotalAmount,0)
        ELSE 0
    END                                            AS PackedRatePct,

    ISNULL(h.IsCreditableConfirmed,0)              AS IsCreditableConfirmed,
    ISNULL(h.Description,'')                       AS Description,

    usd.Rate                                       AS ExchangeRateUSD

FROM dbo.trOrderHeader h

JOIN (
    SELECT
        l.OrderHeaderID,
        SUM(ISNULL(c.NetAmount,0)) AS TotalAmount,
        SUM(
            CASE
                WHEN ISNULL(c.CurrencyCode,'') = 'TRY'
                    THEN ISNULL(c.NetAmount,0)
                ELSE 0
            END
        ) AS TotalTRY,
        SUM(
            CASE
                WHEN ISNULL(l.IsClosed,0) = 1
                    THEN ISNULL(c.NetAmount,0)
                ELSE 0
            END
        ) AS PackedAmount,
        SUM(
            CASE
                WHEN ISNULL(l.IsClosed,0) = 1
                     AND ISNULL(c.CurrencyCode,'') = 'TRY'
                    THEN ISNULL(c.NetAmount,0)
                ELSE 0
            END
        ) AS PackedTRY
    FROM dbo.trOrderLine l
    JOIN dbo.trOrderHeader h2
      ON h2.OrderHeaderID = l.OrderHeaderID
    LEFT JOIN dbo.trOrderLineCurrency c
      ON c.OrderLineID  = l.OrderLineID
     AND c.CurrencyCode = ISNULL(h2.DocCurrencyCode,'TRY')
    GROUP BY l.OrderHeaderID
) l
  ON l.OrderHeaderID = h.OrderHeaderID

LEFT JOIN dbo.cdCurrAccDesc ca
  ON ca.CurrAccCode = h.CurrAccCode
 AND ca.LangCode = 'TR'

LEFT JOIN dbo.CustomerAttributesFilter f
  ON f.CurrAccCode = h.CurrAccCode

LEFT JOIN dbo.cdCurrAccAttributeDesc mt
  ON mt.CurrAccTypeCode    = 3
 AND mt.AttributeTypeCode  = 2
 AND mt.AttributeCode      = f.CustomerAtt02
 AND mt.LangCode           = 'TR'

LEFT JOIN dbo.cdCurrAccAttributeDesc py
  ON py.CurrAccTypeCode    = 3
 AND py.AttributeTypeCode  = 1
 AND py.AttributeCode      = f.CustomerAtt01
 AND py.LangCode           = 'TR'

OUTER APPLY (
    SELECT TOP 1 Rate
    FROM dbo.AllExchangeRates
    WHERE CurrencyCode = 'USD'
      AND RelationCurrencyCode = 'TRY'
      AND ExchangeTypeCode = 6
      AND Rate > 0
      AND Date <= CAST(GETDATE() AS date)
    ORDER BY Date DESC
) usd

OUTER APPLY (
    SELECT TOP 1 Rate
    FROM dbo.AllExchangeRates
    WHERE CurrencyCode = h.DocCurrencyCode
      AND RelationCurrencyCode = 'TRY'
      AND ExchangeTypeCode = 6
      AND Rate > 0
      AND Date <= CAST(GETDATE() AS date)
    ORDER BY Date DESC
) cur

WHERE
    ISNULL(h.IsCancelOrder,0) = 0
    AND h.OrderTypeCode = 1
    AND h.ProcessCode   = 'WS'
    AND h.IsClosed      = 0
    AND ISNULL(l.TotalAmount,0) > 0

    AND EXISTS (
        SELECT 1
        FROM dbo.CustomerAttributesFilter f2
        WHERE f2.CurrAccCode = h.CurrAccCode
          AND %s
    )
`, piyasaWhere)

	if search != "" {
		baseQuery += `
AND EXISTS (
  SELECT 1
  FROM dbo.trOrderHeader h2
  LEFT JOIN dbo.cdCurrAccDesc ca2
    ON ca2.CurrAccCode = h2.CurrAccCode
   AND ca2.LangCode = 'TR'
  WHERE h2.OrderHeaderID = h.OrderHeaderID
    AND (
      LOWER(REPLACE(REPLACE(h2.OrderNumber,'İ','I'),'ı','i'))
        COLLATE Latin1_General_CI_AI LIKE LOWER(@p1)

      OR LOWER(REPLACE(REPLACE(h2.CurrAccCode,'İ','I'),'ı','i'))
        COLLATE Latin1_General_CI_AI LIKE LOWER(@p1)

      OR LOWER(REPLACE(REPLACE(ca2.CurrAccDescription,'İ','I'),'ı','i'))
        COLLATE Latin1_General_CI_AI LIKE LOWER(@p1)

      OR LOWER(REPLACE(REPLACE(h2.Description,'İ','I'),'ı','i'))
        COLLATE Latin1_General_CI_AI LIKE LOWER(@p1)
    )
)
`
	}

	baseQuery += `
ORDER BY h.CreatedDate DESC
`

	if search != "" {
		searchLike := fmt.Sprintf("%%%s%%", search)
		return mssql.Query(baseQuery, searchLike)
	}

	return mssql.Query(baseQuery)
}

func BulkCloseOrders(
	ctx context.Context,
	mssql *sql.DB,
	pg *sql.DB,
	orderNumbers []string,
	updatedBy string,
) (int64, error) {

	piyasaWhere, err := resolvePiyasaWhere(ctx, pg)
	if err != nil {
		return 0, err
	}

	clean := make([]string, 0, len(orderNumbers))
	seen := make(map[string]struct{})

	for _, n := range orderNumbers {
		n = strings.TrimSpace(n)
		if n == "" {
			continue
		}
		key := strings.ToUpper(n)
		if _, ok := seen[key]; ok {
			continue
		}
		seen[key] = struct{}{}
		clean = append(clean, n)
	}

	if len(clean) == 0 {
		return 0, fmt.Errorf("order_numbers is empty")
	}

	if strings.TrimSpace(updatedBy) == "" {
		updatedBy = "SYSTEM"
	}

	placeholders := make([]string, len(clean))
	args := make([]any, 0, len(clean)+1)
	args = append(args, updatedBy) // @p1

	for i, no := range clean {
		placeholders[i] = fmt.Sprintf("@p%d", i+2)
		args = append(args, no)
	}

	tx, err := mssql.BeginTx(ctx, nil)
	if err != nil {
		return 0, fmt.Errorf("begin tx failed: %w", err)
	}
	defer tx.Rollback()

	q := fmt.Sprintf(`
UPDATE h
SET
    h.IsClosed = 1,
    h.LastUpdatedDate = GETDATE(),
    h.LastUpdatedUserName = @p1
FROM dbo.trOrderHeader h
WHERE
    h.IsClosed = 0
    AND h.OrderNumber IN (%s)
    AND EXISTS (
        SELECT 1
        FROM dbo.CustomerAttributesFilter f2
        WHERE f2.CurrAccCode = h.CurrAccCode
          AND %s
    )
    AND EXISTS (
        SELECT 1
        FROM (
            SELECT
                l.OrderHeaderID,
                SUM(ISNULL(c.NetAmount,0)) AS TotalAmount,
                SUM(
                    CASE
                        WHEN ISNULL(l.IsClosed,0) = 1
                            THEN ISNULL(c.NetAmount,0)
                        ELSE 0
                    END
                ) AS PackedAmount
            FROM dbo.trOrderLine l
            JOIN dbo.trOrderHeader h2
              ON h2.OrderHeaderID = l.OrderHeaderID
            LEFT JOIN dbo.trOrderLineCurrency c
              ON c.OrderLineID  = l.OrderLineID
             AND c.CurrencyCode = ISNULL(h2.DocCurrencyCode,'TRY')
            GROUP BY l.OrderHeaderID
        ) t
        WHERE t.OrderHeaderID = h.OrderHeaderID
          AND t.TotalAmount > 0
    );
`, strings.Join(placeholders, ","), piyasaWhere)

	res, err := tx.ExecContext(ctx, q, args...)
	if err != nil {
		return 0, fmt.Errorf("bulk close update failed: %w", err)
	}

	affected, err := res.RowsAffected()
	if err != nil {
		return 0, fmt.Errorf("rows affected read failed: %w", err)
	}

	if err := tx.Commit(); err != nil {
		return 0, fmt.Errorf("commit failed: %w", err)
	}

	return affected, nil
}
