import 'dart:async';

import 'package:oto/oto/application.dart';
import 'package:oto/oto/commands/command.dart';
import 'package:oto/oto/data/command_data.dart';
import 'dart:io';
import 'package:http/http.dart' as http;
// ignore: depend_on_referenced_packages, implementation_imports
import 'package:http_parser/src/media_type.dart';

class WebBase extends Command {
  Future<int> request(DataWebBase data, http.BaseRequest request) async {
    var c = Completer<int>();
    if (data.headers != null) {
      for (var head in data.headers!.entries) {
        request.headers[head.key] = head.value;
      }
    }
    var response = await request.send();
    var exitCode = 0;
    data.successCodes ??= [];
    if ((response.statusCode < 300 && response.statusCode >= 200) ||
        data.successCodes!.contains(response.statusCode)) {
      exitCode = 0;
      if (data.setResponse != null) {
        var responseBody = await response.stream.bytesToString();
        await setProperty(data.setResponse!, responseBody);
      }
    } else {
      exitCode = response.statusCode;
    }
    c.complete(exitCode);
    return c.future;
  }
}

class WebRequest extends WebBase {
  @override
  Future execute(DataCommand command) async {
    var data = DataWebBase.fromJson(getParam(command));
    Application.log('Request URL: ${data.url}');
    data.method ??= 'GET';
    var request = http.Request(data.method!, Uri.parse(data.url));
    if (data.fields != null) {
      for (var field in data.fields!.entries) {
        request.bodyFields[field.key] = field.value;
      }
    }
    return await complete(await super.request(data, request), command);
  }
}

class WebFile extends WebBase {
  @override
  Future execute(DataCommand command) async {
    var data = DataWebFile.fromJson(getParam(command));
    Application.log('Upload URL: ${data.url}');

    var form = http.MultipartRequest('POST', Uri.parse(data.url));
    if (data.fields != null) {
      for (var field in data.fields!.entries) {
        form.fields[field.key] = field.value;
      }
    }
    if (data.filePart != null &&
        data.fileMimeType != null &&
        data.files != null) {
      for (var filePath in data.files!) {
        var file = File(filePath);
        if (file.existsSync()) {
          form.files.add(await http.MultipartFile.fromPath(
              data.filePart!, file.path,
              contentType: MediaType(
                  HttpHeaders.contentTypeHeader, data.fileMimeType!)));
        } else {
          throw Exception('File not found: $filePath');
        }
      }
    }
    return await complete(await super.request(data, form), command);
  }
}

void registerWebCommands() {
  Command.register(
    CommandType.WebRequest,
    () => WebRequest(),
    spec: const CommandSpec(
      type: CommandType.WebRequest,
      category: 'web',
      dataModel: 'DataWebBase',
      samplePath: 'assets/yaml/sample/09_network.yaml',
    ),
  );
  Command.register(
    CommandType.WebFile,
    () => WebFile(),
    spec: const CommandSpec(
      type: CommandType.WebFile,
      category: 'web',
      dataModel: 'DataWebFile',
      samplePath: 'assets/yaml/sample/09_network.yaml',
    ),
  );
}
