package packs

import (
	"bytes"
	"context"
	"crypto/sha256"
	"database/sql"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"strings"

	"github.com/pingcap-inc/nexuscrm-connectors/features/connector/capabilities"
	"github.com/pingcap-inc/nexuscrm-connectors/features/connector/inboundhandlerconfig/configwire"
	"github.com/pingcap-inc/nexuscrm-controlplane/internal/offlinecandidateauthority"
)

// ConnectorInboundHandlerConfigComponentPayload is the manifest payload for one
// connector_inbound_handler_config component. The config can be either one
// shared capability config or an endpointConfigs map keyed by webhook_secret
// name when a connector pack needs endpoint-specific verifier/parser behavior
// while retaining one durable row per (InstalledPackId, CapabilityKind).
type ConnectorInboundHandlerConfigComponentPayload struct {
	Namespace      string          `json:"namespace"`
	CapabilityKind string          `json:"capabilityKind"`
	Config         json.RawMessage `json:"config"`
}

type connectorInboundHandlerConfigRow struct {
	ID              string
	InstalledPackID string
	Namespace       string
	CapabilityKind  string
	ConfigJSON      string
	IsDeleted       int
}

type connectorInboundHandlerConfigComponent struct {
	packName          string
	manifestComponent ManifestComponent
	payload           ConnectorInboundHandlerConfigComponentPayload
	rawPayload        []byte
	configJSON        string
}

var _ componentCustomVerifier = (*connectorInboundHandlerConfigComponent)(nil)

func newConnectorInboundHandlerConfigComponent(packName string, component ManifestComponent, payload []byte) (PackComponent, error) {
	var p ConnectorInboundHandlerConfigComponentPayload
	dec := json.NewDecoder(bytes.NewReader(payload))
	dec.DisallowUnknownFields()
	if err := dec.Decode(&p); err != nil {
		return nil, fmt.Errorf("parse connector_inbound_handler_config payload %s: %w", componentPayloadLabel(component), err)
	}
	if err := ensureJSONEOF(dec); err != nil {
		return nil, fmt.Errorf("connector_inbound_handler_config payload %s has trailing data: %w", componentPayloadLabel(component), err)
	}
	p.Namespace = strings.TrimSpace(p.Namespace)
	p.CapabilityKind = strings.TrimSpace(p.CapabilityKind)
	kind := connectorcapabilities.CapabilityKind(p.CapabilityKind)
	if !kind.IsKnown() {
		return nil, fmt.Errorf("connector_inbound_handler_config %s/%s has unknown capability kind", p.Namespace, p.CapabilityKind)
	}
	canonical, err := configwire.ValidateCapabilityConfig(p.Namespace, p.CapabilityKind, p.Config)
	if err != nil {
		return nil, fmt.Errorf("validate connector_inbound_handler_config %s/%s semantic config: %w", p.Namespace, p.CapabilityKind, err)
	}
	return &connectorInboundHandlerConfigComponent{
		packName:          packName,
		manifestComponent: component,
		payload:           p,
		rawPayload:        append([]byte(nil), payload...),
		configJSON:        canonical,
	}, nil
}

func (c *connectorInboundHandlerConfigComponent) Kind() ComponentKind {
	return ComponentKindConnectorInboundHandlerConfig
}

func (c *connectorInboundHandlerConfigComponent) PackName() string {
	return c.packName
}

func (c *connectorInboundHandlerConfigComponent) BusinessKey() string {
	return c.payload.Namespace + ":" + c.payload.CapabilityKind
}

func (c *connectorInboundHandlerConfigComponent) Manageability() Manageability {
	return ManageabilityLocked
}

func (c *connectorInboundHandlerConfigComponent) ExpectedPayload() ([]byte, error) {
	return append([]byte(nil), c.rawPayload...), nil
}

func (c *connectorInboundHandlerConfigComponent) ExpectedProjection() (any, error) {
	return connectorInboundHandlerConfigRow{
		Namespace:      c.payload.Namespace,
		CapabilityKind: c.payload.CapabilityKind,
		ConfigJSON:     c.configJSON,
		IsDeleted:      0,
	}, nil
}

func (c *connectorInboundHandlerConfigComponent) LiveProjection(ctx context.Context, deps VerifyDeps) (any, error) {
	if deps.DB == nil {
		return nil, fmt.Errorf("database is required")
	}
	var row connectorInboundHandlerConfigRow
	err := deps.DB.QueryRowContext(ctx, `
SELECT Id, InstalledPackId, Namespace, CapabilityKind, ConfigJSON, COALESCE(IsDeleted, 0)
  FROM _System_ConnectorInboundHandlerConfig
 WHERE Namespace = ?
   AND CapabilityKind = ?
   AND COALESCE(IsDeleted, 0) = 0
 ORDER BY LastModifiedDate DESC
 LIMIT 1`,
		c.payload.Namespace, c.payload.CapabilityKind,
	).Scan(&row.ID, &row.InstalledPackID, &row.Namespace, &row.CapabilityKind, &row.ConfigJSON, &row.IsDeleted)
	if err == sql.ErrNoRows {
		return nil, nil
	}
	if err != nil {
		return nil, fmt.Errorf("query connector inbound handler config %s/%s: %w", c.payload.Namespace, c.payload.CapabilityKind, err)
	}
	return row, nil
}

func (c *connectorInboundHandlerConfigComponent) VerifyComponent(ctx context.Context, deps VerifyDeps) error {
	if deps.DB == nil {
		return fmt.Errorf("database is required")
	}
	packInstallID, err := resolveConnectorInboundHandlerConfigPackInstallID(ctx, deps.DB, c.packName)
	if err != nil {
		return fmt.Errorf("VerifyComponent: %w", err)
	}
	var row connectorInboundHandlerConfigRow
	err = deps.DB.QueryRowContext(ctx, `
SELECT Id, InstalledPackId, Namespace, CapabilityKind, ConfigJSON, COALESCE(IsDeleted, 0)
  FROM _System_ConnectorInboundHandlerConfig
 WHERE InstalledPackId = ?
   AND CapabilityKind = ?
   AND COALESCE(IsDeleted, 0) = 0
 LIMIT 1`,
		packInstallID, c.payload.CapabilityKind,
	).Scan(&row.ID, &row.InstalledPackID, &row.Namespace, &row.CapabilityKind, &row.ConfigJSON, &row.IsDeleted)
	if err == sql.ErrNoRows {
		return fmt.Errorf("connector inbound handler config not found: pack=%s kind=%s packInstallId=%s", c.packName, c.payload.CapabilityKind, packInstallID)
	}
	if err != nil {
		return fmt.Errorf("verify connector inbound handler config pack=%s kind=%s: %w", c.packName, c.payload.CapabilityKind, err)
	}
	expectedID := connectorInboundHandlerConfigID(packInstallID, c.payload.CapabilityKind)
	if row.ID != expectedID {
		return fmt.Errorf("connector inbound handler config Id mismatch for kind %s: got %q want %q", c.payload.CapabilityKind, row.ID, expectedID)
	}
	if row.Namespace != c.payload.Namespace {
		return fmt.Errorf("connector inbound handler config Namespace mismatch: got %q want %q", row.Namespace, c.payload.Namespace)
	}
	if row.InstalledPackID != packInstallID {
		return fmt.Errorf("connector inbound handler config InstalledPackId mismatch: got %q want %q", row.InstalledPackID, packInstallID)
	}
	if row.CapabilityKind != c.payload.CapabilityKind {
		return fmt.Errorf("connector inbound handler config CapabilityKind mismatch: got %q want %q", row.CapabilityKind, c.payload.CapabilityKind)
	}
	if row.ConfigJSON != c.configJSON {
		return fmt.Errorf("connector inbound handler config ConfigJSON drift for kind %s", c.payload.CapabilityKind)
	}
	if row.IsDeleted != 0 {
		return fmt.Errorf("connector inbound handler config row for kind %s is soft-deleted (IsDeleted=%d)", c.payload.CapabilityKind, row.IsDeleted)
	}
	return nil
}

func (c *connectorInboundHandlerConfigComponent) Apply(ctx context.Context, deps ApplyDeps) error {
	if deps.DB == nil {
		return fmt.Errorf("database is required")
	}
	packInstallID, err := resolveConnectorInboundHandlerConfigPackInstallID(ctx, deps.DB, c.packName)
	if err != nil {
		return err
	}
	canonical, err := configwire.ValidateCapabilityConfig(c.payload.Namespace, c.payload.CapabilityKind, c.payload.Config)
	if err != nil {
		return fmt.Errorf("canonicalize connector inbound handler config %s/%s: %w", c.payload.Namespace, c.payload.CapabilityKind, err)
	}
	id := connectorInboundHandlerConfigID(packInstallID, c.payload.CapabilityKind)
	_, err = deps.DB.ExecContext(ctx, `
INSERT INTO _System_ConnectorInboundHandlerConfig
  (Id, InstalledPackId, Namespace, CapabilityKind, ConfigJSON, IsDeleted, CreatedDate, LastModifiedDate)
VALUES (?, ?, ?, ?, ?, 0, NOW(), NOW())
ON DUPLICATE KEY UPDATE
  Namespace = VALUES(Namespace),
  ConfigJSON = VALUES(ConfigJSON),
  IsDeleted = 0,
  LastModifiedDate = NOW()`,
		id, packInstallID, c.payload.Namespace, c.payload.CapabilityKind, canonical,
	)
	if err != nil {
		return fmt.Errorf("upsert connector inbound handler config %s/%s for pack %s: %w", c.payload.Namespace, c.payload.CapabilityKind, c.packName, err)
	}
	return nil
}

func resolveConnectorInboundHandlerConfigPackInstallID(ctx context.Context, db *sql.DB, packName string) (string, error) {
	if packInstallID, ok := offlinecandidateauthority.StoppedSourceSetPackInstallID(ctx, packName); ok {
		return packInstallID, nil
	}
	var packInstallID string
	err := db.QueryRowContext(ctx, `
    SELECT PackInstallId
      FROM _System_InstalledPack
     WHERE Name = ?
       AND COALESCE(IsDeleted, 0) = 0
       AND Status IN ('installing', 'active')
     ORDER BY LastModifiedDate DESC
     LIMIT 1`, packName).Scan(&packInstallID)
	if err == sql.ErrNoRows {
		return "", fmt.Errorf("connector_inbound_handler_config: pack %q has no in-flight installation record (Apply called outside install lifecycle?)", packName)
	}
	if err != nil {
		return "", fmt.Errorf("connector_inbound_handler_config: lookup PackInstallId for pack %q: %w", packName, err)
	}
	if strings.TrimSpace(packInstallID) == "" {
		return "", fmt.Errorf("connector_inbound_handler_config: pack %q has empty PackInstallId (upgrade backfill not applied)", packName)
	}
	return packInstallID, nil
}

func connectorInboundHandlerConfigID(packInstallID, capabilityKind string) string {
	sum := sha256.Sum256([]byte(
		strings.ToLower(strings.TrimSpace(packInstallID)) + "\x00" +
			strings.ToLower(strings.TrimSpace(capabilityKind)),
	))
	return "cihc_" + hex.EncodeToString(sum[:])
}
