package services

import (
	"context"
	"errors"

	"github.com/nexuscrm/automation"
	appErrors "github.com/nexuscrm/kernel/pkg/errors"
	"github.com/nexuscrm/shared/pkg/constants"
	"github.com/nexuscrm/shared/pkg/contract"
	"github.com/nexuscrm/shared/pkg/models"
)

const connectorSyncStatusInternalMessage = "An internal server error occurred. Contact your system administrator."

func (sm *ServiceManager) ConnectorSyncStatusContract() contract.ConnectorSyncStatus {
	if sm == nil {
		return nil
	}
	if sm.NeutralConnectorSyncStatus != nil {
		return sm.NeutralConnectorSyncStatus
	}
	if sm.Permissions == nil || sm.Persistence == nil || sm.DB() == nil {
		return nil
	}
	return sm
}

func (sm *ServiceManager) GetConnectorSyncStatus(ctx context.Context, user *models.UserSession, objectName string, recordID string) (*contract.ConnectorSyncStatusResult, error) {
	if sm == nil || sm.Permissions == nil || sm.Persistence == nil || sm.DB() == nil {
		return nil, connectorSyncStatusInternal()
	}
	if user == nil {
		return nil, connectorSyncStatusForbidden("Insufficient privileges to access connector sync status")
	}
	if !sm.Permissions.CheckSystemPermission(ctx, user, string(constants.PermViewConnectorDiagnostics)) {
		return nil, connectorSyncStatusForbidden("Insufficient privileges to access connector sync status")
	}

	if _, _, err := sm.Persistence.RetrieveWithAccess(ctx, objectName, recordID, user); err != nil {
		return nil, connectorSyncStatusReadError(err)
	}

	states, err := automation.NewSQLConnectorPushDurableStore(sm.DB()).FindRecordPushState(ctx, automation.ConnectorPushRecordStateQuery{
		SourceObject:   objectName,
		SourceRecordID: recordID,
	})
	if err != nil {
		return nil, connectorSyncStatusInternal()
	}

	out := make([]contract.ConnectorSyncStatusRow, 0, len(states))
	for _, state := range states {
		row := contract.ConnectorSyncStatusRow{
			ConnectorOwnerKey: state.ConnectorOwnerKey,
			ActionType:        state.ActionType,
			DestinationSlotID: state.DestinationSlotID,
			TargetObject:      state.TargetObject,
		}
		if state.HasLink && state.LinkStatus != "" {
			value := state.LinkStatus
			row.LinkStatus = &value
		}
		if state.HasAttempt && state.LastAttemptStatus != "" {
			value := state.LastAttemptStatus
			row.LastAttemptStatus = &value
		}
		if state.HasAttempt && state.ResponseStatusCode.Valid {
			value := state.ResponseStatusCode.Int64
			row.ResponseStatusCode = &value
		}
		if state.HasAttempt && state.LastErrorAt.Valid {
			value := state.LastErrorAt.Time
			row.LastErrorAt = &value
		}
		out = append(out, row)
	}

	return &contract.ConnectorSyncStatusResult{
		Object:       objectName,
		RecordID:     recordID,
		SyncStatuses: out,
	}, nil
}

func connectorSyncStatusReadError(err error) error {
	if err == nil {
		return nil
	}
	if appErrors.IsInternal(err) {
		return connectorSyncStatusInternal()
	}
	var hs interface{ HTTPStatus() int }
	if errors.As(err, &hs) && hs.HTTPStatus() >= 400 && hs.HTTPStatus() < 500 {
		return &contract.ConnectorSyncStatusError{
			Kind:    contract.ConnectorSyncStatusErrorNotFound,
			Message: "The requested resource does not exist",
		}
	}
	return connectorSyncStatusInternal()
}

func connectorSyncStatusForbidden(message string) error {
	return &contract.ConnectorSyncStatusError{
		Kind:    contract.ConnectorSyncStatusErrorForbidden,
		Message: message,
	}
}

func connectorSyncStatusInternal() error {
	return &contract.ConnectorSyncStatusError{
		Kind:    contract.ConnectorSyncStatusErrorInternal,
		Message: connectorSyncStatusInternalMessage,
	}
}

var _ contract.ConnectorSyncStatus = (*ServiceManager)(nil)
