import 'package:flutter/material.dart';
import 'package:flutter_flow_chart/flutter_flow_chart.dart';
import 'package:oc_front/main.dart';
import 'package:oc_front/models/abstract.dart';
import 'package:oc_front/models/resources/compute.dart';
import 'package:oc_front/models/resources/data.dart';
import 'package:oc_front/models/resources/processing.dart';
import 'package:oc_front/models/resources/storage.dart';
import 'package:oc_front/models/resources/workflow.dart';

class Resource implements SerializerDeserializer<Resource> {
  List<DataItem> datas = [];
  List<ProcessingItem> processings = [];
  List<StorageItem> storages = [];
  List<ComputeItem> computes = [];
  List<WorkflowItem> workflows = [];

  Resource({
    this.datas = const [],
    this.processings = const [],
    this.storages = const [],
    this.computes = const [],
    this.workflows = const [],
  });

  @override Resource deserialize(json) {
    try { json = json as Map<String, dynamic>;
    } catch (e) { return Resource(); }
    return Resource(
      computes: json.containsKey("compute_resource") ? fromListJson(json["compute_resource"], ComputeItem()) : [],
      datas: json.containsKey("data_resource") ? fromListJson(json["data_resource"], DataItem()) : [],
      processings: json.containsKey("processing_resource") ? fromListJson(json["processing_resource"], ProcessingItem()) : [],
      storages: json.containsKey("storage_resource") ? fromListJson(json["storage_resource"], StorageItem()) : [],
      workflows: json.containsKey("workflow_resource") ? fromListJson(json["workflow_resource"], WorkflowItem()) : [],
    );
  }

  @override Map<String, dynamic> serialize() {
    return {
      "compute_resource": toListJson<ComputeItem>(computes),
      "data_resource": toListJson<DataItem>(datas),
      "processing_resource": toListJson<ProcessingItem>(processings),
      "storage_resource": toListJson<StorageItem>(storages),
      "workflow_resource": toListJson<WorkflowItem>(workflows),
    };
  }
}

class Owner extends SerializerDeserializer<Owner> {
  String? name;
  String? logo;

  Owner({
    this.name,
    this.logo,
  });

  @override Owner deserialize(json) {
    try { json = json as Map<String, dynamic>;
    } catch (e) { return Owner(); }
    return Owner(
      name: json.containsKey("name") ? json["name"] : null,
      logo: json.containsKey("logo") ? json["logo"] : null,
    );
  }

  @override Map<String, dynamic> serialize() {
    return {
      "name": name,
      "logo": logo,
    };
  }
}

abstract class Infos {
  Map<String, dynamic> infos();
}

class Artifact extends SerializerDeserializer<Artifact> {
  String? attrPath;
  String? attrFrom;
  bool readOnly = true;

  Artifact({
    this.attrPath,
    this.attrFrom,
    this.readOnly = true,
  });

  @override Artifact deserialize(json) {
    try { json = json as Map<String, dynamic>;
    } catch (e) { return Artifact(); }
    return Artifact(
      attrPath: json.containsKey("attr_path") ? json["attr_path"] : null,
      attrFrom: json.containsKey("attr_from") ? json["attr_from"] : null,
      readOnly: json.containsKey("readonly") ? json["readonly"] : true,
    );
  }

  @override Map<String, dynamic> serialize() {
    return {
      "attr_path": attrPath,
      "attr_from": attrFrom,
      "readonly": readOnly,
    };
  }
}
class Param extends SerializerDeserializer<Param> {
  String? name;
  String? attr;
  dynamic value;
  String? origin;
  bool optionnal = false;
  bool readOnly = true;

  Param({
    this.name,
    this.attr,
    this.value,
    this.origin,
    this.optionnal = false,
    this.readOnly = true,
  });

  @override Param deserialize(json) {
    try { json = json as Map<String, dynamic>;
    } catch (e) { return Param(); }
    return Param(
      name: json.containsKey("name") ? json["name"] : null,
      attr: json.containsKey("attr") ? json["attr"] : null,
      value: json.containsKey("value") ? json["value"] : null,
      origin: json.containsKey("origin") ? json["origin"] : null,
      optionnal: json.containsKey("optionnal") ? json["optionnal"] : false,
      readOnly: json.containsKey("readonly") ? json["readonly"] : false,
    );
  }

  @override Map<String, dynamic> serialize() {
    return {
      "name": name,
      "attr": attr,
      "value": value,
      "origin": origin,
      "optionnal": optionnal,
      "readonly": readOnly,
    };
  }
}

abstract class AbstractItem<X extends AbstractPricing, Y extends AbstractPartnerShip<X>, S extends AbstractInstance<X,Y>, T extends FlowData> extends FlowData implements SerializerDeserializer<T>, Infos {
  String? id;
  String? name;
  String? logo;
  String? type;
  String? creatorID;
  String? updaterID;
  DateTime? createdAt;
  DateTime? updatedAt;
  List<Owner> owners;
  String? description;
  String? restrictions;
  String? shortDescription;
  int selectedInstance = 0;

  List<AbstractInstance<X,Y>> instances = [];

  String get topic => "";

  AbstractItem({
    this.id,
    this.type,
    this.name,
    this.logo,
    this.creatorID,
    this.updaterID,
    this.createdAt,
    this.updatedAt,
    this.description,
    this.shortDescription,
    this.owners = const [],
    this.selectedInstance = 0,
  });

  void addEnv(List<dynamic> infos) {
    var inst = getSelectedInstance();
    if (inst == null) { return; }
    inst.env = [];
    for (var info in infos) {
      inst.env.add(Param(name: info["name"], attr: info["attr"], value: info["value"], 
      origin: info["origin"], optionnal: info["optionnal"], readOnly: info["readonly"]));
    }
  }

  AbstractInstance<X,Y>? getSelectedInstance() {
    if (selectedInstance == -1) { return instances.isEmpty ? null : instances[0]; }
    return  instances.isNotEmpty ? instances[selectedInstance] : null;
  }

  @override String getID() {
    return id ?? "";
  }

  @override String getType() {
    return type ?? "";
  }


  @override String getName() {
    return name ?? "";
  }

  @override
  Map<String, dynamic> infos();

  double? width;
  double? height;
  @override
  double? getWidth() {
    return width;
  }
  @override
  double? getHeight() {
    return height;
  }

  Map<String, dynamic> setVariable(String key, dynamic value, Map<String, dynamic> map) {
    map[key] = value;
    return map;
  }

  dynamic getVariable(String key, Map<String, dynamic> map) {
    return map[key];
  }

  Map<String, dynamic> toJSON() {
    return {
      "id": id,
      "type": type ?? topic,
      "name": name,
      "logo": logo,
      "owners": toListJson(owners),
      "creator_id": creatorID,
      "updater_id": updaterID,
      "creation_date": createdAt?.toIso8601String(),
      "update_date": updatedAt?.toIso8601String(),
      "description": description,
      "short_description": shortDescription,
      "selected_instance": selectedInstance,
      "instances": instances.map((e) => e.serialize()).toList(),
    };
  }

  void mapFromJSON(dynamic json, S ex) {
    try { json = json as Map<String, dynamic>;
    } catch (e) { return; }   
    this.id = json.containsKey("id") ? json["id"] : null;
    this.type = json.containsKey("type") ? json["type"] : topic;
    this.name = json.containsKey("name") ? json["name"] : null;
    this.logo = json.containsKey("logo") ? json["logo"] : null;
    this.creatorID = json.containsKey("creator_id") ? json["creator_id"] : null;
    this.updaterID = json.containsKey("updater_id") ? json["updater_id"] : null;
    this.description = json.containsKey("description") ? json["description"] : null;
    this.owners = json.containsKey("owners") ? fromListJson(json["owners"], Owner()) : [];
    this.instances = json.containsKey("instances") ? fromListJson(json["instances"], ex) : [];
    this.updatedAt = json.containsKey("update_date") ? DateTime.parse(json["update_date"]) : null;
    this.selectedInstance = json.containsKey("selected_instance") ? json["selected_instance"] : 0;
    this.shortDescription = json.containsKey("short_description") ? json["short_description"] : null;
    this.createdAt = json.containsKey("creation_date") ? DateTime.parse(json["creation_date"]) : null;
  }
}

class Location extends SerializerDeserializer<Location> {
  double? latitude;
  double? longitude;

  Location({
    this.latitude,
    this.longitude,
  });

  @override
  Location deserialize(json) {
    return Location(
      latitude: json.containsKey("latitude") ? json["latitude"] : null,
      longitude: json.containsKey("longitude") ? json["longitude"] : null,
    );
  }
  @override
  Map<String, dynamic> serialize() {
    return {
      "latitude": latitude,
      "longitude": longitude,
    };
  }
}
  
abstract class AbstractInstance<X extends AbstractPricing, S extends AbstractPartnerShip<X>> extends SerializerDeserializer<AbstractInstance<X,S>> implements Infos {
  String? id;
  String? name; 
  int? countryCode;
  Location? location;
  List<S> partnerships = [];
  List<Param> env = [];
  List<Param> inputs = [];
  List<Param> outputs = [];

  bool isEnv(String key) {
    for (var e in env) {
      if (e.name?.contains(key) ?? false || key.contains(e.name ?? "none")) { return true; }
    }
    return false;
  }

  bool isEnvAttr(String attr, String origin, bool isOrigin) {
    for (var e in env) {
      if (e.attr == attr && ((isOrigin && e.origin != null) || (!isOrigin && e.origin == origin))) { return true; }
    }
    return false;
  }

  @override
  Map<String, dynamic> infos();

  void mapFromJSON(dynamic json, S ex) {
    try { json = json as Map<String, dynamic>;
    } catch (e) { return; } 
    this.countryCode = json.containsKey("country_code") ? json["country_code"] : null;
    this.id = json.containsKey("id") ? json["id"] : null;
    this.name = json.containsKey("name") ? json["name"] : null;
    this.env = json.containsKey("env") ? fromListJson(json["env"], Param()) : [];
    this.inputs = json.containsKey("inputs") ? fromListJson(json["inputs"], Param()) : [];
    this.outputs = json.containsKey("outputs") ? fromListJson(json["outputs"], Param()) : [];
    this.location = json.containsKey("location") ? Location().deserialize(json["location"]) : null;
    this.partnerships = json.containsKey("partnerships") ? fromListJson(json["partnerships"], ex) : [];
  }

  Map<String, dynamic> toJSON() {
    return {
      "country_code": countryCode,
      "id": id,
      "name": name,
      "location": location?.serialize(),
      "env": toListJson(env),
      "inputs": toListJson(inputs),
      "outputs": toListJson(outputs),
      "partnerships": partnerships.map((e) => e.serialize()).toList(),
    }; 
  }
}

abstract class AbstractPartnerShip<S extends AbstractPricing> extends SerializerDeserializer<AbstractPartnerShip<S>> {
  String? namespace;
  List<AbstractPricing> pricings = [];

  void mapFromJSON(dynamic json, S ex) {
    try { json = json as Map<String, dynamic>;
    } catch (e) { return; } 
    this.namespace = json.containsKey("namespace") ? json["namespace"] : null;
    this.pricings = json.containsKey("pricings") ? fromListJson(json["pricings"], ex) : [];
  }

  Map<String, dynamic> toJSON() {
    return {
      "namespace": namespace,
      "pricings": pricings,
    }; 
  }
}

abstract class AbstractPricing extends SerializerDeserializer<AbstractPricing> {
  PricingStrategy? pricing;
  int? refundTypeEnum;
  int? refundRatio;
  List<dynamic> additionnalRefundTypeEnum = [];
  int? privilegeStrategyEnum;
  int? garantedDelaySecond;
  bool exceeding = false;
  int? exceedingRatio;

  void mapFromJSON(dynamic json) {
    try { json = json as Map<String, dynamic>;
    } catch (e) { return; } 
    pricing = json.containsKey("pricing") ? PricingStrategy().deserialize(json["pricing"]) : null;
    refundTypeEnum = json.containsKey("refund_type") ? json["refund_type"] : null;
    refundRatio = json.containsKey("refund_ratio") ? json["refund_ratio"] : null;
    additionnalRefundTypeEnum = json.containsKey("additionnal_refund_type") ? json["additionnal_refund_type"] : [];
    privilegeStrategyEnum = json.containsKey("privilege_strategy") ? json["privilege_strategy"] : null;
    garantedDelaySecond = json.containsKey("garanted_delay") ? json["garanted_delay"] : null;
    exceeding = json.containsKey("exceeding") ? json["exceeding"] : false;
    exceedingRatio = json.containsKey("exceeding_ratio") ? json["exceeding_ratio"] : null;
  }

  Map<String, dynamic> toJSON() {
    return {
      "pricing": pricing?.serialize(),
      "refund_type": refundTypeEnum,
      "refund_ratio": refundRatio,
      "additionnal_refund_type": additionnalRefundTypeEnum,
      "privilege_strategy": privilegeStrategyEnum,
      "garanted_delay": garantedDelaySecond,
      "exceeding": exceeding,
      "exceeding_ratio": exceedingRatio,
    }; 
  }
}

class PricingStrategy extends SerializerDeserializer<PricingStrategy> {
  double? price;
  String? currency;
  int? buyingStrategyEnum;
  int? timeStrategyEnum;
  int? overrideStrategyEnum;

  PricingStrategy({
    this.price,
    this.currency,
    this.buyingStrategyEnum,
    this.timeStrategyEnum,
    this.overrideStrategyEnum,
  });

  @override
  PricingStrategy deserialize(dynamic json) {
    return PricingStrategy(
      price: json.containsKey("price") && json["price"] != null ? json["price"] : null,
      currency: json.containsKey("currency") && json["currency"] != null  ? json["currency"] : null,
      buyingStrategyEnum: json.containsKey("buying_strategy") ? json["buying_strategy"] : null,
      timeStrategyEnum: json.containsKey("time_strategy") ? json["time_strategy"] : null,
      overrideStrategyEnum: json.containsKey("override_strategy") ? json["override_strategy"] : null,
    );
  }
  @override
  Map<String, dynamic> serialize() {
    return {
      "price": price,
      "currency": currency,
      "buying_strategy": buyingStrategyEnum,
      "time_strategy": timeStrategyEnum,
      "override_strategy": overrideStrategyEnum,
    }; 
  }
}

class Model extends SerializerDeserializer<Model> {
  dynamic value;
  String? type;
  bool readonly = false;

  Model({
    this.value,
    this.type,
    this.readonly = false,
  });

  @override deserialize(dynamic json) {
    try { json = json as Map<String, dynamic>;
    } catch (e) { return Model(); }
    return Model(
      value: json.containsKey("value") ? json["value"] : null,
      type: json.containsKey("type") ? json["type"] : null,
      readonly: json.containsKey("readonly") ? json["readonly"] : false,
    );
  }
  @override Map<String, dynamic> serialize() => {
    "value": value,
    "type": type,
    "readonly": readonly,
  };
}

Type? getTopicType(String topic) {
  if (topic == "processing") { return ProcessingItem; }
  else if (topic == "data") { return DataItem; }
  else if (topic == "compute") { return ComputeItem; }
  else if (topic == "storage") { return StorageItem; }
  else if (topic == "workflow") { return WorkflowItem; }
  else { return null; }
}

String getTopic(Type type) {
  if (type == AbstractItem) { return "resource"; }
  if (type == ProcessingItem) { return "processing"; }
  if (type == DataItem) { return "data"; }
  if (type == ComputeItem) { return "compute"; }
  if (type == StorageItem) { return "storage"; }
  if (type == WorkflowItem) { return "workflow"; }
  return "";
}

bool isComputing(String topic) => topic == "processing";
bool isData(String topic) => topic == "data";
bool isCompute(String topic) => topic == "compute";
bool isStorage(String topic) => topic == "storage";
bool isWorkflow(String topic) => topic == "workflow";

Color getColor(String topic) => isData(topic) ? Colors.blue : isComputing(topic) ? Colors.green : 
                                isCompute(topic) ? Colors.orange : isStorage(topic) ? redColor : Colors.grey;