import 'package:flutter/material.dart';

import 'oto_console_models.dart';

export 'oto_console_models.dart';

class OtoConsoleConfig {
  final String serverHttpUrl;
  final String serverWireUrl;
  final String? authTokenReference;

  const OtoConsoleConfig({
    required this.serverHttpUrl,
    required this.serverWireUrl,
    this.authTokenReference,
  });
}

class OtoConsoleThemeAdapter {
  final Color primaryColor;
  final Color accentColor;
  final Color backgroundColor;
  final Color railBackgroundColor;
  final Color borderColor;
  final Color textColor;
  final Color textSecondaryColor;

  const OtoConsoleThemeAdapter({
    this.primaryColor = const Color(0xFF22D3EE),
    this.accentColor = const Color(0xFFF59E0B),
    this.backgroundColor = const Color(0xFF111318),
    this.railBackgroundColor = const Color(0xFF181B22),
    this.borderColor = const Color(0xFF2A2F3A),
    this.textColor = Colors.white,
    this.textSecondaryColor = Colors.white60,
  });
}

typedef OtoConsoleNavigationCallback = void Function(OtoConsoleSection section);

class OtoConsoleNavigation {
  final OtoConsoleNavigationCallback? onNavigate;

  const OtoConsoleNavigation({this.onNavigate});
}

class OtoCapability {
  final String name;
  final String description;
  final IconData? icon;

  const OtoCapability({
    required this.name,
    required this.description,
    this.icon,
  });
}

class OtoCapabilityPack {
  final List<OtoCapability> capabilities;

  const OtoCapabilityPack({required this.capabilities});
}

enum OtoCoreConnectionState { checking, online, degraded, offline }

class OtoCoreEndpointStatus {
  final String label;
  final String path;
  final OtoCoreConnectionState state;
  final int? statusCode;
  final String message;

  const OtoCoreEndpointStatus({
    required this.label,
    required this.path,
    required this.state,
    this.statusCode,
    required this.message,
  });

  const OtoCoreEndpointStatus.checking({
    required this.label,
    required this.path,
  }) : state = OtoCoreConnectionState.checking,
       statusCode = null,
       message = 'Checking';

  bool get ok => state == OtoCoreConnectionState.online;
}

class OtoCoreConnectionSnapshot {
  final OtoCoreConnectionState state;
  final OtoCoreEndpointStatus health;
  final OtoCoreEndpointStatus readiness;
  final DateTime? checkedAt;
  final String policyNote;

  const OtoCoreConnectionSnapshot({
    required this.state,
    required this.health,
    required this.readiness,
    this.checkedAt,
    this.policyNote = '',
  });

  const OtoCoreConnectionSnapshot.checking()
    : state = OtoCoreConnectionState.checking,
      health = const OtoCoreEndpointStatus.checking(
        label: 'Health',
        path: '/healthz',
      ),
      readiness = const OtoCoreEndpointStatus.checking(
        label: 'Readiness',
        path: '/readyz',
      ),
      checkedAt = null,
      policyNote = '';

  bool get connected => state == OtoCoreConnectionState.online;

  String get label {
    return switch (state) {
      OtoCoreConnectionState.checking => 'Checking Core',
      OtoCoreConnectionState.online => 'Core online',
      OtoCoreConnectionState.degraded => 'Core degraded',
      OtoCoreConnectionState.offline => 'Core offline',
    };
  }
}

abstract class OtoCoreConnectionClient {
  Future<OtoCoreConnectionSnapshot> check(OtoConsoleConfig config);
}

enum OtoSurfaceLoadState { loading, empty, error, data }

class OtoSurfaceSnapshot<T> {
  final OtoSurfaceLoadState state;
  final T? data;
  final String? errorMessage;

  const OtoSurfaceSnapshot({required this.state, this.data, this.errorMessage});

  const OtoSurfaceSnapshot.loading()
    : state = OtoSurfaceLoadState.loading,
      data = null,
      errorMessage = null;

  const OtoSurfaceSnapshot.empty()
    : state = OtoSurfaceLoadState.empty,
      data = null,
      errorMessage = null;

  const OtoSurfaceSnapshot.error(String message)
    : state = OtoSurfaceLoadState.error,
      data = null,
      errorMessage = message;

  const OtoSurfaceSnapshot.data(this.data)
    : state = OtoSurfaceLoadState.data,
      errorMessage = null;
}

class OtoRunnerViewModel {
  final String runnerID;
  final String alias;
  final String status;
  final String currentJobID;
  final String currentExecutionID;
  final String message;

  const OtoRunnerViewModel({
    required this.runnerID,
    required this.alias,
    required this.status,
    required this.currentJobID,
    required this.currentExecutionID,
    required this.message,
  });
}

class OtoJobViewModel {
  final String jobID;
  final String name;
  final String state;
  final String executionID;
  final String createdAt;
  final String updatedAt;

  const OtoJobViewModel({
    required this.jobID,
    required this.name,
    required this.state,
    required this.executionID,
    required this.createdAt,
    required this.updatedAt,
  });
}

class OtoExecutionViewModel {
  final String executionID;
  final String jobID;
  final String state;
  final String runnerID;
  final String createdAt;
  final String updatedAt;

  const OtoExecutionViewModel({
    required this.executionID,
    required this.jobID,
    required this.state,
    required this.runnerID,
    required this.createdAt,
    required this.updatedAt,
  });
}

class OtoLogEntryViewModel {
  final String timestamp;
  final String line;

  const OtoLogEntryViewModel({
    required this.timestamp,
    required this.line,
  });
}

class OtoArtifactViewModel {
  final String name;
  final String path;

  const OtoArtifactViewModel({
    required this.name,
    required this.path,
  });
}

// === Action State Contract ===

enum OtoActionStatus { idle, submitting, succeeded, deferred, failed }

// === Execution Action Drafts ===

class OtoExecutionCancelDraft {
  final String executionID;
  final String? runnerID;
  final String? reason;

  const OtoExecutionCancelDraft({
    required this.executionID,
    this.runnerID,
    this.reason,
  });

  Map<String, dynamic> toJson() {
    final map = <String, dynamic>{'execution_id': executionID};
    if (runnerID != null && runnerID!.isNotEmpty) {
      map['runner_id'] = runnerID;
    }
    if (reason != null && reason!.isNotEmpty) {
      map['reason'] = reason;
    }
    return map;
  }
}

class OtoExecutionReportDraft {
  final String executionID;
  final String? runnerID;
  final String? jobID;
  final bool success;
  final int? exitCode;
  final String? message;

  const OtoExecutionReportDraft({
    required this.executionID,
    this.runnerID,
    this.jobID,
    required this.success,
    this.exitCode,
    this.message,
  });

  Map<String, dynamic> toJson() {
    final map = <String, dynamic>{
      'execution_id': executionID,
      'success': success,
    };
    if (runnerID != null && runnerID!.isNotEmpty) {
      map['runner_id'] = runnerID;
    }
    if (jobID != null && jobID!.isNotEmpty) {
      map['job_id'] = jobID;
    }
    if (exitCode != null) {
      map['exit_code'] = exitCode;
    }
    if (message != null && message!.isNotEmpty) {
      map['message'] = message;
    }
    return map;
  }
}

class OtoExecutionLogDraft {
  final String executionID;
  final String? runnerID;
  final String line;

  const OtoExecutionLogDraft({
    required this.executionID,
    this.runnerID,
    required this.line,
  });

  Map<String, dynamic> toJson() {
    final map = <String, dynamic>{
      'execution_id': executionID,
      'line': line,
    };
    if (runnerID != null && runnerID!.isNotEmpty) {
      map['runner_id'] = runnerID;
    }
    return map;
  }
}

class OtoExecutionArtifactDraft {
  final String executionID;
  final String? runnerID;
  final String name;
  final String path;

  const OtoExecutionArtifactDraft({
    required this.executionID,
    this.runnerID,
    required this.name,
    required this.path,
  });

  Map<String, dynamic> toJson() {
    final map = <String, dynamic>{
      'execution_id': executionID,
      'name': name,
      'path': path,
    };
    if (runnerID != null && runnerID!.isNotEmpty) {
      map['runner_id'] = runnerID;
    }
    return map;
  }
}

class OtoActionViewState {
  final OtoActionStatus status;
  final String? message;

  const OtoActionViewState({
    required this.status,
    this.message,
  });

  const OtoActionViewState.idle() : this(status: OtoActionStatus.idle);

  const OtoActionViewState.submitting({String? message})
    : this(status: OtoActionStatus.submitting, message: message);

  const OtoActionViewState.succeeded({String? message})
    : this(status: OtoActionStatus.succeeded, message: message);

  const OtoActionViewState.deferred({String? message})
    : this(status: OtoActionStatus.deferred, message: message);

  const OtoActionViewState.failed({required String message})
    : this(status: OtoActionStatus.failed, message: message);

  bool get isIdle => status == OtoActionStatus.idle;
  bool get isSubmitting => status == OtoActionStatus.submitting;
  bool get isSuccess => status == OtoActionStatus.succeeded;
  bool get isDeferred => status == OtoActionStatus.deferred;
  bool get isFailed => status == OtoActionStatus.failed;
}

typedef OtoRunnerActionState = OtoActionViewState;

class OtoRunnerSelfUpdateDraft {
  final String runnerID;
  final String version;
  final String downloadUrl;

  const OtoRunnerSelfUpdateDraft({
    required this.runnerID,
    required this.version,
    required this.downloadUrl,
  });

  bool get isValidUrl => downloadUrl.startsWith('https://');
  bool get isValid => runnerID.isNotEmpty && version.isNotEmpty && isValidUrl;

  Map<String, dynamic> toJson() => {
        'runner_id': runnerID,
        'version': version,
        'download_url': downloadUrl,
      };
}

class OtoJobCreateDraft {
  final String id;
  final String name;
  final Map<String, dynamic>? runRequest;

  const OtoJobCreateDraft({
    required this.id,
    required this.name,
    this.runRequest,
  });

  bool get isValid => id.trim().isNotEmpty && name.trim().isNotEmpty;

  Map<String, dynamic> toJson() {
    final map = <String, dynamic>{'id': id, 'name': name};
    if (runRequest != null) {
      map['run_request'] = runRequest;
    }
    return map;
  }
}
