// RUN: %empty-directory(%t)

// REQUIRES: concurrency

func manyWithError() async throws -> (String, Int) { ("", 0) }
func manyWithError(_ completion: @escaping (String?, Int?, Error?) -> Void) { }

func mixed() async -> (String, Int) { ("", 0) }
func mixed(_ completion: @escaping (String?, Int) -> Void) { }

func mixedError() async throws -> (String, Int) { ("", 0) }
func mixedError(_ completion: @escaping (String?, Int, Error?) -> Void) { }

func testParamsMulti() async throws {
  // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=MANYBOUND %s
  manyWithError { res1, res2, err in
    print("before")
    if let bad = err {
      print("got error \(bad)")
      return
    }
    if let str = res1, let i = res2 {
      print("got result \(str)")
      print("got result \(i)")
    }
    print("after")
  }
  // MANYBOUND: do {
  // MANYBOUND-NEXT: let (str, i) = try await manyWithError()
  // MANYBOUND-NEXT: print("before")
  // MANYBOUND-NEXT: print("got result \(str)")
  // MANYBOUND-NEXT: print("got result \(i)")
  // MANYBOUND-NEXT: print("after")
  // MANYBOUND-NEXT: } catch let bad {
  // MANYBOUND-NEXT: print("got error \(bad)")
  // MANYBOUND-NEXT: }

  // FIXME: This case is a little tricky: Being in the else block of 'if let str = res1'
  // should allow us to place 'if let i = res2' in the failure block. However, this
  // is a success condition, so we still place it in the success block. Really what
  // we need to do here is check to see if manyWithError has an existing async
  // alternative that still returns optional success values, and allow success
  // classification in that case. Otherwise, we'd probably be better off leaving
  // the condition unhandled, as it's not clear what the user is doing.
  // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=MANYUNBOUND-ERR %s
  manyWithError { res1, res2, err in
    print("before")
    if let str = res1 {
      print("got result \(str)")
    } else if let i = res2 {
      print("got result \(i)")
    } else {
      print("got error \(err!)")
    }
    print("after")
  }
  // MANYUNBOUND-ERR: do {
  // MANYUNBOUND-ERR-NEXT: let (str, i) = try await manyWithError()
  // MANYUNBOUND-ERR-NEXT: print("before")
  // MANYUNBOUND-ERR-NEXT: print("got result \(str)")
  // MANYUNBOUND-ERR-NEXT: print("got result \(i)")
  // MANYUNBOUND-ERR-NEXT: print("after")
  // MANYUNBOUND-ERR-NEXT: } catch let err {
  // MANYUNBOUND-ERR-NEXT: print("got error \(err)")
  // MANYUNBOUND-ERR-NEXT: }

  // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=MANYBOUND %s
  manyWithError { res1, res2, err in
    print("before")
    if let bad = err {
      print("got error \(bad)")
      return
    }
    if let str = res1 {
      print("got result \(str)")
    }
    if let i = res2 {
      print("got result \(i)")
    }
    print("after")
  }

  // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=MIXED-COND %s
  manyWithError { res1, res2, err in
    print("before")
    if res1 != nil && res2 == nil {
      print("got result \(res1!)")
    }
    print("after")
  }
  //  MIXED-COND: convert_params_multi.swift
  //  MIXED-COND-NEXT: let (res1, res2) = try await manyWithError()
  //  MIXED-COND-NEXT: print("before")
  //  MIXED-COND-NEXT: if <#res1#> != nil && <#res2#> == nil {
  //  MIXED-COND-NEXT: print("got result \(res1)")
  //  MIXED-COND-NEXT: }
  //  MIXED-COND-NEXT: print("after")

  // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=MIXED-CONDELSE %s
  manyWithError { res1, res2, err in
    print("before")
    if res1 != nil && res2 == nil {
      print("got result \(res1!)")
    } else {
      print("bad")
    }
    print("after")
  }
  // MIXED-CONDELSE: var res1: String? = nil
  // MIXED-CONDELSE-NEXT: var res2: Int? = nil
  // MIXED-CONDELSE-NEXT: var err: Error? = nil
  // MIXED-CONDELSE-NEXT: do {
  // MIXED-CONDELSE-NEXT: (res1, res2) = try await manyWithError()
  // MIXED-CONDELSE-NEXT: } catch {
  // MIXED-CONDELSE-NEXT: err = error
  // MIXED-CONDELSE-NEXT: }
  // MIXED-CONDELSE-EMPTY:
  // MIXED-CONDELSE-NEXT: print("before")
  // MIXED-CONDELSE-NEXT: if res1 != nil && res2 == nil {
  // MIXED-CONDELSE-NEXT: print("got result \(res1!)")
  // MIXED-CONDELSE-NEXT: } else {
  // MIXED-CONDELSE-NEXT: print("bad")
  // MIXED-CONDELSE-NEXT: }
  // MIXED-CONDELSE-NEXT: print("after")

  // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=MANYUNBOUND-ERR %s
  manyWithError { res1, res2, err in
    print("before")
    guard let str = res1, let i = res2 else {
      print("got error \(err!)")
      return
    }
    print("got result \(str)")
    print("got result \(i)")
    print("after")
  }

  // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=MANYUNBOUND %s
  manyWithError { res1, res2, err in
    print("before")
    guard res1 != nil && res2 != nil && err == nil else {
      print("got error \(err!)")
      return
    }
    print("got result \(res1!)")
    print("got result \(res2!)")
    print("after")
  }
  // MANYUNBOUND: do {
  // MANYUNBOUND-NEXT: let (res1, res2) = try await manyWithError()
  // MANYUNBOUND-NEXT: print("before")
  // MANYUNBOUND-NEXT: print("got result \(res1)")
  // MANYUNBOUND-NEXT: print("got result \(res2)")
  // MANYUNBOUND-NEXT: print("after")
  // MANYUNBOUND-NEXT: } catch let err {
  // MANYUNBOUND-NEXT: print("got error \(err)")
  // MANYUNBOUND-NEXT: }

  // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=MANYUNBOUND %s
  manyWithError { res1, res2, err in
    print("before")
    guard res1 != nil else {
      print("got error \(err!)")
      return
    }
    print("got result \(res1!)")
    print("got result \(res2!)")
    print("after")
  }

  // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=MANYUNBOUND %s
  manyWithError { res1, res2, err in
    print("before")
    guard err == nil else {
      print("got error \(err!)")
      return
    }
    print("got result \(res1!)")
    print("got result \(res2!)")
    print("after")
  }

  // Cannot use refactor-check-compiles, as cannot use non-optional 'str' in
  // optional binding.
  // RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=MIXED %s
  mixed { str, num in
    print("before")
    if let res = str {
      print("got result \(res)")
    }
    print("\(num)")
    print("after")
  }
  // MIXED: convert_params_multi.swift
  // MIXED-NEXT: let (str, num) = await mixed()
  // MIXED-NEXT: print("before")
  // MIXED-NEXT: if let res = str {
  // MIXED-NEXT: print("got result \(res)")
  // MIXED-NEXT: }
  // MIXED-NEXT: print("\(num)")
  // MIXED-NEXT: print("after")
  // MIXED-NOT: }

  // RUN: %refactor-check-compiles -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=MIXED-ERROR %s
  mixedError { str, num, err in
    print("before")
    if let res = str {
      print("got result \(res)")
    } else {
      print("got \(err!)")
    }
    print("\(num)")
    print("after")
  }
  // MIXED-ERROR: convert_params_multi.swift
  // MIXED-ERROR-NEXT: do {
  // MIXED-ERROR-NEXT: let (res, num) = try await mixedError()
  // MIXED-ERROR-NEXT: print("before")
  // MIXED-ERROR-NEXT: print("got result \(res)")
  // MIXED-ERROR-NEXT: print("\(num)")
  // MIXED-ERROR-NEXT: print("after")
  // MIXED-ERROR-NEXT: } catch let err {
  // MIXED-ERROR-NEXT: print("got \(err)")
  // MIXED-ERROR-NEXT: }
  // MIXED-ERROR-NOT: }
}
