package linthost

import (
  "os"
  "path/filepath"
  "strings"
  "testing"
)

// TestCommandFormatBraceOwnershipNegativeTwins pins what taking ownership of a
// shared-line brace must NOT reach.
//
// The stranded-brace fix gives `format/indent` a brace and a member header
// that no rule claimed before, so its risk is over-reach: every one of these
// shapes is either already what Prettier 3.8.3 emits, or a frame the format
// rules deliberately hand to `format/print-width`. Each must survive `ttsc
// format` byte-for-byte.
//
//  1. Seed the source.
//  2. Run `ttsc format`.
//  3. Assert the file is unchanged.
//
// @evidence contracts/testing.md#behavioral-verification The format command must leave each canonical empty, inline-type, else/catch/finally and commented-brace fixture byte-identical and converge successfully.
// @evidence contracts/testing.md#independent-expectations These literals record the supported canonical shapes; comparing full source preserves statements, comments and brace placement instead of merely checking idempotency.
// @evidence contracts/testing.md#distinguishing-cases Eight named subcases distinguish empty bodies, inline types, canonical chained braces and comments. The stranded-brace CRLF sibling owns an input that must change.
// @evidence contracts/testing.md#execution-ownership TestCommandFormatBraceOwnershipNegativeTwins is a public format unit selected by the lint semantic-unit Evidence claim. The isolated fixture filesystem feeds the actual Go command entry in the shared process. This verifies command semantics without compiling or launching a native artifact or installing a consumer.
func TestCommandFormatBraceOwnershipNegativeTwins(t *testing.T) {
  for _, tc := range []struct {
    name   string
    source string
  }{
    // Prettier keeps an empty body on one line; there is no statement to
    // break out, so there is nothing for the brace to be consistent with.
    {"empty-block", "export function f() {}\n"},
    {"empty-interface", "export interface Gamma {}\n"},
    {"empty-class", "export class Empty {}\n"},
    {"empty-method-body", "export class C {\n  m() {}\n}\n"},
    // An object TYPE keeps the author's shape when the source wrote no break
    // after `{`, so its members must not be broken out the way a class or
    // interface body's are.
    {"inline-type-literal", "export type T = { a: number };\n"},
    // `} else {`, `} catch (e) {`, `} finally {` are the canonical Prettier
    // spellings: the `}` is already the first byte on its line, so the
    // shared-line rule must read the brace's own line, not the line's tail.
    {
      "canonical-else-catch-finally",
      "export function f(n: number) {\n" +
        "  if (n) {\n" +
        "    a();\n" +
        "  } else {\n" +
        "    b();\n" +
        "  }\n" +
        "  try {\n" +
        "    c();\n" +
        "  } catch (e) {\n" +
        "    d();\n" +
        "  } finally {\n" +
        "    e();\n" +
        "  }\n" +
        "}\n",
    },
    // A block whose brace-line gap carries a comment. Both new claims walk
    // back over spaces and tabs only, so a comment bounds the run and cannot
    // be swallowed; this asserts that rather than leaving it incidental.
    {
      "comment-before-claimed-brace",
      "export function f() {\n  a(); /* trailing */\n}\n",
    },
    {
      "line-comment-above-claimed-brace",
      "export function f() {\n  a(); // trailing\n}\n",
    },
  } {
    t.Run(tc.name, func(t *testing.T) {
      root := seedLintProject(t, tc.source)
      seedLintConfig(t, root, map[string]any{"format": map[string]any{}})
      main := filepath.Join(root, "src", "main.ts")

      code, _, stderr := captureCommandOutput(t, func() int {
        return run([]string{"format", "--cwd", root, "--plugins-json", lintManifest(t)})
      })
      if code != 0 || strings.Contains(stderr, "did not converge") {
        t.Fatalf("format did not converge: code=%d stderr=%q", code, stderr)
      }
      got, err := os.ReadFile(main)
      if err != nil {
        t.Fatalf("ReadFile: %v", err)
      }
      if string(got) != tc.source {
        t.Fatalf("source must survive unchanged:\ngot  %q\nwant %q", string(got), tc.source)
      }
    })
  }
}
