package platform

import (
	"context"
	"database/sql"
	"encoding/json"
	"fmt"
	"strings"
	"time"
)

const accessEvidenceLimit = 50000

func buildAccessUnresolvedIdentityWhere(query AccessUnresolvedIdentityQuery) (string, []any) {
	where := []string{
		`COALESCE(NULLIF(TRIM(b.vin),''),'')=''`,
		`(r.vin IS NULL OR TRIM(r.vin)='' OR LOWER(TRIM(r.vin))='unknown')`,
	}
	args := []any{}
	if keyword := strings.TrimSpace(query.Keyword); keyword != "" {
		like := "%" + keyword + "%"
		where = append(where, `(r.phone LIKE ? OR r.plate LIKE ? OR r.manufacturer LIKE ? OR r.source_endpoint LIKE ?)`)
		args = append(args, like, like, like, like)
	}
	return strings.Join(where, " AND "), args
}

func (s *ProductionStore) AccessUnresolvedIdentities(ctx context.Context, query AccessUnresolvedIdentityQuery) (Page[AccessUnresolvedIdentity], error) {
	where, args := buildAccessUnresolvedIdentityWhere(query)
	from := ` FROM jt808_registration r LEFT JOIN vehicle_identity_binding b ON b.phone=r.phone WHERE ` + where
	var total int
	if err := s.db.QueryRowContext(ctx, `SELECT COUNT(*)`+from, args...).Scan(&total); err != nil {
		return Page[AccessUnresolvedIdentity]{}, err
	}
	listArgs := append(append([]any(nil), args...), query.Limit, query.Offset)
	rows, err := s.db.QueryContext(ctx, `SELECT
SHA2(CONCAT('JT808:',r.phone),256),
'JT808',
CASE WHEN CHAR_LENGTH(r.phone)>=7 THEN CONCAT(LEFT(r.phone,3),'****',RIGHT(r.phone,4)) ELSE '***' END,
COALESCE(r.plate,''),COALESCE(r.manufacturer,''),COALESCE(r.source_endpoint,''),
COALESCE(DATE_FORMAT(r.first_registered_at,'%Y-%m-%d %H:%i:%s'),''),
COALESCE(DATE_FORMAT(r.latest_registered_at,'%Y-%m-%d %H:%i:%s'),''),
COALESCE(DATE_FORMAT(r.latest_authenticated_at,'%Y-%m-%d %H:%i:%s'),''),
COALESCE(DATE_FORMAT(r.latest_seen_at,'%Y-%m-%d %H:%i:%s'),''),
GREATEST(0,TIMESTAMPDIFF(SECOND,r.latest_seen_at,NOW())),
'missing_vin_jt808',
'核对终端手机号、车牌和厂家后维护 phone→VIN 权威绑定；禁止猜测 VIN'
`+from+` ORDER BY r.latest_seen_at DESC,r.phone ASC LIMIT ? OFFSET ?`, listArgs...)
	if err != nil {
		return Page[AccessUnresolvedIdentity]{}, err
	}
	defer rows.Close()
	items := make([]AccessUnresolvedIdentity, 0, query.Limit)
	for rows.Next() {
		var item AccessUnresolvedIdentity
		if err := rows.Scan(&item.ID, &item.Protocol, &item.IdentifierMasked, &item.Plate, &item.Manufacturer, &item.SourceEndpoint, &item.FirstRegisteredAt, &item.LatestRegisteredAt, &item.LatestAuthenticatedAt, &item.LatestSeenAt, &item.FreshnessSec, &item.IssueCode, &item.RecommendedAction); err != nil {
			return Page[AccessUnresolvedIdentity]{}, err
		}
		items = append(items, item)
	}
	return Page[AccessUnresolvedIdentity]{Items: items, Total: total, Limit: query.Limit, Offset: query.Offset}, rows.Err()
}

func (s *ProductionStore) AccessEvidence(ctx context.Context) ([]AccessEvidenceRow, error) {
	rows, err := s.db.QueryContext(ctx, `SELECT
v.vin,
COALESCE(NULLIF(s.plate, ''), NULLIF(b.plate, ''), '') AS plate,
COALESCE(NULLIF(b.oem, ''), '') AS oem,
COALESCE(p.model_name, '') AS model_name,
COALESCE(p.company_name, '') AS company_name,
COALESCE(s.protocol, '') AS protocol,
COALESCE(s.platform_name, '') AS provider,
COALESCE(DATE_FORMAT(s.access_first_seen_at, '%Y-%m-%d %H:%i:%s.%f'), '') AS first_seen_at,
COALESCE(s.access_first_seen_source, '') AS first_seen_source,
COALESCE(DATE_FORMAT(s.event_time, '%Y-%m-%d %H:%i:%s.%f'), '') AS event_time,
COALESCE(DATE_FORMAT(s.access_latest_received_at, '%Y-%m-%d %H:%i:%s.%f'), '') AS received_at,
COALESCE(DATE_FORMAT(s.access_latest_received_at, '%Y-%m-%d %H:%i:%s.%f'), '') AS updated_at,
COALESCE(s.access_report_interval_ms, -1) AS report_interval_ms,
COALESCE(s.access_sample_count, 0) AS report_sample_count,
COALESCE(s.event_id, '') AS event_id,
COALESCE(l.longitude, 0) AS longitude,
COALESCE(l.latitude, 0) AS latitude,
COALESCE(l.speed_kmh, 0) AS speed_kmh,
COALESCE(l.soc_percent, 0) AS soc_percent,
COALESCE(l.total_mileage_km, 0) AS total_mileage_km,
COALESCE(m.daily_mileage_km, 0) AS daily_mileage_km
FROM (
  SELECT DISTINCT vin FROM vehicle_identity_binding WHERE vin IS NOT NULL AND vin <> ''
) v
LEFT JOIN vehicle_identity_binding b ON b.vin = v.vin
LEFT JOIN vehicle_profile p ON p.vin = v.vin
LEFT JOIN vehicle_realtime_snapshot s ON s.vin = v.vin
LEFT JOIN vehicle_realtime_location l ON l.vin = s.vin AND l.protocol = s.protocol
LEFT JOIN (
  SELECT vin, protocol, MAX(daily_mileage_km) AS daily_mileage_km
  FROM vehicle_daily_mileage
  WHERE stat_date = CURDATE()
  GROUP BY vin, protocol
) m ON m.vin = s.vin AND m.protocol = s.protocol
ORDER BY v.vin ASC, s.protocol ASC
LIMIT ?`, accessEvidenceLimit+1)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	items := make([]AccessEvidenceRow, 0, 1024)
	for rows.Next() {
		var row AccessEvidenceRow
		var reportIntervalMS int64
		if err := rows.Scan(&row.VIN, &row.Plate, &row.OEM, &row.Model, &row.Company, &row.Protocol, &row.Provider, &row.FirstSeenAt, &row.FirstSeenSource, &row.LatestEventAt, &row.LatestReceivedAt, &row.LatestUpdatedAt, &reportIntervalMS, &row.ReportSampleCount, &row.LatestEventID, &row.Longitude, &row.Latitude, &row.SpeedKmh, &row.SOCPercent, &row.TotalMileageKm, &row.DailyMileageKm); err != nil {
			return nil, err
		}
		if reportIntervalMS >= 0 {
			seconds := int((reportIntervalMS + 500) / 1000)
			row.ReportIntervalSec = &seconds
		}
		if row.Protocol == "" {
			row.Source = "vehicle_identity_binding"
			row.LatestError = "车辆已建档但从未形成实时快照"
		} else {
			row.Source = "vehicle_realtime_snapshot"
			switch row.FirstSeenSource {
			case "live_writer":
				row.FirstSeenEvidence = "网关实时写入首次观测"
			case "snapshot_backfill":
				row.FirstSeenEvidence = "上线基线：由实时快照回填（非历史首次接入）"
			}
			if row.ReportSampleCount >= 2 && row.ReportIntervalSec != nil {
				row.ReportIntervalProof = fmt.Sprintf("网关连续接收时间差（持久样本 %d 条）", row.ReportSampleCount)
			} else if row.ReportSampleCount == 1 {
				row.ReportIntervalProof = "仅有 1 个持久接收样本，等待下一次上报"
			}
		}
		items = append(items, row)
		if len(items) > accessEvidenceLimit {
			return nil, fmt.Errorf("access evidence exceeds safety limit %d", accessEvidenceLimit)
		}
	}
	return items, rows.Err()
}

func (s *ProductionStore) ensureAccessSchema(ctx context.Context) error {
	s.accessSchemaOnce.Do(func() {
		statements := []string{
			`CREATE TABLE IF NOT EXISTS vehicle_access_threshold_config (
  id TINYINT NOT NULL PRIMARY KEY,
  version INT NOT NULL,
  default_threshold_sec INT NOT NULL,
  delay_threshold_sec INT NOT NULL,
  long_offline_sec INT NOT NULL,
  protocol_overrides_json LONGTEXT NOT NULL,
  updated_by VARCHAR(128) NOT NULL,
  updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
)`,
			`CREATE TABLE IF NOT EXISTS vehicle_access_threshold_audit (
  id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
  version INT NOT NULL,
  actor VARCHAR(128) NOT NULL,
  summary VARCHAR(255) NOT NULL,
  config_json LONGTEXT NOT NULL,
  changed_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
  KEY idx_access_threshold_audit_version (version),
  KEY idx_access_threshold_audit_changed (changed_at)
)`,
		}
		for _, statement := range statements {
			if _, err := s.db.ExecContext(ctx, statement); err != nil {
				s.accessSchemaErr = err
				return
			}
		}
		defaults := defaultAccessThresholds(time.Now())
		protocols, _ := json.Marshal(defaults.Protocols)
		_, s.accessSchemaErr = s.db.ExecContext(ctx, `INSERT IGNORE INTO vehicle_access_threshold_config
(id, version, default_threshold_sec, delay_threshold_sec, long_offline_sec, protocol_overrides_json, updated_by)
VALUES (1, ?, ?, ?, ?, ?, ?)`, defaults.Version, defaults.DefaultThresholdSec, defaults.DelayThresholdSec, defaults.LongOfflineSec, string(protocols), defaults.UpdatedBy)
	})
	return s.accessSchemaErr
}

func (s *ProductionStore) AccessThresholds(ctx context.Context) (AccessThresholdConfig, error) {
	if err := s.ensureAccessSchema(ctx); err != nil {
		return AccessThresholdConfig{}, err
	}
	var config AccessThresholdConfig
	var protocolsJSON string
	err := s.db.QueryRowContext(ctx, `SELECT version, default_threshold_sec, delay_threshold_sec, long_offline_sec,
protocol_overrides_json, updated_by, DATE_FORMAT(updated_at, '%Y-%m-%d %H:%i:%s')
FROM vehicle_access_threshold_config WHERE id = 1`).Scan(
		&config.Version, &config.DefaultThresholdSec, &config.DelayThresholdSec, &config.LongOfflineSec,
		&protocolsJSON, &config.UpdatedBy, &config.UpdatedAt,
	)
	if err != nil {
		return AccessThresholdConfig{}, err
	}
	if err := json.Unmarshal([]byte(protocolsJSON), &config.Protocols); err != nil {
		return AccessThresholdConfig{}, fmt.Errorf("decode access protocol thresholds: %w", err)
	}
	config.UpdatedAt = normalizeAccessTime(config.UpdatedAt)
	auditRows, err := s.db.QueryContext(ctx, `SELECT version, actor, DATE_FORMAT(changed_at, '%Y-%m-%d %H:%i:%s'), summary
FROM vehicle_access_threshold_audit ORDER BY changed_at DESC, id DESC LIMIT 10`)
	if err != nil {
		return AccessThresholdConfig{}, err
	}
	defer auditRows.Close()
	for auditRows.Next() {
		var item AccessThresholdAudit
		if err := auditRows.Scan(&item.Version, &item.Actor, &item.ChangedAt, &item.Summary); err != nil {
			return AccessThresholdConfig{}, err
		}
		item.ChangedAt = normalizeAccessTime(item.ChangedAt)
		config.Audit = append(config.Audit, item)
	}
	return config, auditRows.Err()
}

func (s *ProductionStore) SaveAccessThresholds(ctx context.Context, update AccessThresholdUpdate) (AccessThresholdConfig, error) {
	if err := s.ensureAccessSchema(ctx); err != nil {
		return AccessThresholdConfig{}, err
	}
	tx, err := s.db.BeginTx(ctx, &sql.TxOptions{})
	if err != nil {
		return AccessThresholdConfig{}, err
	}
	defer tx.Rollback()
	var currentVersion int
	if err := tx.QueryRowContext(ctx, `SELECT version FROM vehicle_access_threshold_config WHERE id = 1 FOR UPDATE`).Scan(&currentVersion); err != nil {
		return AccessThresholdConfig{}, err
	}
	if currentVersion != update.Version {
		return AccessThresholdConfig{}, clientError{Code: "ACCESS_THRESHOLD_VERSION_CONFLICT", Message: "阈值配置已被其他用户更新，请刷新后重试"}
	}
	protocols, err := json.Marshal(update.Protocols)
	if err != nil {
		return AccessThresholdConfig{}, err
	}
	nextVersion := currentVersion + 1
	result, err := tx.ExecContext(ctx, `UPDATE vehicle_access_threshold_config SET
version = ?, default_threshold_sec = ?, delay_threshold_sec = ?, long_offline_sec = ?,
protocol_overrides_json = ?, updated_by = ?, updated_at = CURRENT_TIMESTAMP
WHERE id = 1 AND version = ?`, nextVersion, update.DefaultThresholdSec, update.DelayThresholdSec, update.LongOfflineSec, string(protocols), update.Actor, currentVersion)
	if err != nil {
		return AccessThresholdConfig{}, err
	}
	if affected, err := result.RowsAffected(); err != nil || affected != 1 {
		return AccessThresholdConfig{}, clientError{Code: "ACCESS_THRESHOLD_VERSION_CONFLICT", Message: "阈值配置更新冲突，请刷新后重试"}
	}
	snapshot, _ := json.Marshal(map[string]any{
		"version": nextVersion, "defaultThresholdSec": update.DefaultThresholdSec,
		"delayThresholdSec": update.DelayThresholdSec, "longOfflineSec": update.LongOfflineSec,
		"protocols": update.Protocols,
	})
	if _, err := tx.ExecContext(ctx, `INSERT INTO vehicle_access_threshold_audit
(version, actor, summary, config_json) VALUES (?, ?, ?, ?)`, nextVersion, update.Actor, "更新在线、延迟和长离线阈值", string(snapshot)); err != nil {
		return AccessThresholdConfig{}, err
	}
	if err := tx.Commit(); err != nil {
		return AccessThresholdConfig{}, err
	}
	return s.AccessThresholds(ctx)
}
