import 'package:commons_core/commons_core.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:tane/db/enums.dart';
import 'package:tane/i18n/strings.g.dart';
import 'package:tane/services/profile_store.dart';
import 'package:tane/services/social_account_store.dart';
import 'package:tane/services/social_connection.dart';
import 'package:tane/services/social_service.dart';
import 'package:tane/services/social_settings.dart';
import 'package:drift/drift.dart' show Value;
import 'package:tane/domain/crop_calendar.dart';
import 'package:tane/ui/about_screen.dart';
import 'package:tane/ui/calendar_screen.dart';
import 'package:tane/ui/chat_screen.dart';
import 'package:tane/ui/home_screen.dart';
import 'package:tane/ui/intro_screen.dart';
import 'package:tane/ui/inventory_list_screen.dart';
import 'package:tane/ui/market_screen.dart';
import 'package:tane/ui/plantares_screen.dart';
import 'package:tane/ui/profile_screen.dart';
import 'package:tane/ui/sales_screen.dart';
import 'package:tane/ui/settings_screen.dart';

import '../support/test_support.dart';

/// Small-phone layout guard. A Flutter `RenderFlex overflowed` (a clipped title,
/// a save button pushed off a non-scrolling column, a Row too wide) is reported
/// as an error and FAILS the test — so pumping each screen at a tiny viewport,
/// in the languages whose text runs longest (es/pt/ast), catches exactly the
/// "the save button / title isn't fully visible on a small phone" reports.
///
/// FULL SCREENS ONLY. Modal bottom sheets (quick-add, the lot editor) are NOT
/// checked here: `showModalBottomSheet` gets an unbounded height in a widget
/// test, which fabricates a huge false overflow. Those sheets already wrap their
/// body in a `SingleChildScrollView`, so their save buttons stay reachable;
/// verify them on a real small device.
void main() {
  // iPhone-SE-class logical viewport: narrow AND short, where clipping shows.
  const small = Size(320, 568);
  // The locales whose strings are longest — English rarely overflows first.
  const longLocales = [AppLocale.es, AppLocale.pt, AppLocale.ast];

  setUpAll(() => PackageInfo.setMockInitialValues(
        appName: 'Tanemaki',
        packageName: 'org.comunes.tane',
        version: '0.1.0',
        buildNumber: '1',
        buildSignature: '',
      ));

  Future<void> pumpSmall(WidgetTester tester, Widget widget) async {
    tester.view.physicalSize = small;
    tester.view.devicePixelRatio = 1.0;
    addTearDown(tester.view.resetPhysicalSize);
    addTearDown(tester.view.resetDevicePixelRatio);
    // flutter_test's headless engine can't decode our real PNG assets (the
    // logo), so `Image.asset` throws from the "image resource service". That's
    // unrelated to layout — swallow ONLY those, and let every other FlutterError
    // (crucially the "rendering library" RenderFlex overflow this guard exists
    // to catch) still fail the test. Installed here, inside the running test,
    // because testWidgets reinstalls its own onError at test start (a setUp
    // override would be clobbered).
    final previous = FlutterError.onError;
    FlutterError.onError = (details) {
      if (details.library == 'image resource service') return;
      previous?.call(details);
    };
    addTearDown(() => FlutterError.onError = previous);
    await tester.pumpWidget(widget);
    await tester.pumpAndSettle();
  }

  for (final locale in longLocales) {
    final tag = locale.languageCode;

    testWidgets('home menu fits a small screen ($tag)', (tester) async {
      final db = newTestDatabase();
      addTearDown(db.close);
      await pumpSmall(
        tester,
        wrapScreen(
          repository: newTestRepository(db),
          locale: locale,
          child: const HomeScreen(marketEnabled: true),
        ),
      );
      await disposeTree(tester);
    });

    testWidgets('settings (language, backup, about) fits ($tag)', (tester) async {
      final db = newTestDatabase();
      addTearDown(db.close);
      await pumpSmall(
        tester,
        wrapScreen(
          repository: newTestRepository(db),
          locale: locale,
          child: const SettingsScreen(),
        ),
      );
      await disposeTree(tester);
    });

    testWidgets('about (title, version, license) fits ($tag)', (tester) async {
      final db = newTestDatabase();
      addTearDown(db.close);
      await pumpSmall(
        tester,
        wrapScreen(
          repository: newTestRepository(db),
          locale: locale,
          child: const AboutScreen(),
        ),
      );
      await disposeTree(tester);
    });

    testWidgets('the intro carousel fits ($tag)', (tester) async {
      final db = newTestDatabase();
      addTearDown(db.close);
      await pumpSmall(
        tester,
        wrapScreen(
          repository: newTestRepository(db),
          locale: locale,
          child: IntroScreen(onDone: () {}),
        ),
      );
      await disposeTree(tester);
    });
  }

  testWidgets('profile (identity card + save button) fits a small screen',
      (tester) async {
    final db = newTestDatabase();
    addTearDown(db.close);
    final social = await SocialService.fromRootSeedHex('00' * 32);
    final connection = SocialConnection(
      social: social,
      settings: SocialSettings(InMemorySecretStore()),
      open: (_) async => throw StateError('offline'),
      online: const Stream.empty(),
    );
    await pumpSmall(
      tester,
      wrapScreen(
        repository: newTestRepository(db),
        locale: AppLocale.es,
        child: ProfileScreen(
          social: social,
          connection: connection,
          profileStore: ProfileStore(InMemorySecretStore()),
          accounts: SocialAccountStore(InMemorySecretStore()),
        ),
      ),
    );
    await disposeTree(tester);
  });

  // The market/chat are "live" screens; checked here in their OFFLINE state
  // (the shared connection can't reach a relay) — a static layout, and the one
  // a user without signal actually sees.
  Future<SocialConnection> offlineConn() async {
    final social = await SocialService.fromRootSeedHex('00' * 32);
    final settings = SocialSettings(InMemorySecretStore());
    await settings.setRelayUrls(const []);
    return SocialConnection(
      social: social,
      settings: settings,
      open: (_) async => throw StateError('offline'),
      online: const Stream.empty(),
    );
  }

  testWidgets('market (offline "set up sharing") fits a small screen',
      (tester) async {
    final db = newTestDatabase();
    addTearDown(db.close);
    final connection = await offlineConn();
    await pumpSmall(
      tester,
      wrapScreen(
        repository: newTestRepository(db),
        locale: AppLocale.es,
        child: MarketScreen(
          social: await SocialService.fromRootSeedHex('00' * 32),
          settings: SocialSettings(InMemorySecretStore()),
          connection: connection,
        ),
      ),
    );
    await disposeTree(tester);
  });

  testWidgets('chat (offline "set up sharing") fits a small screen',
      (tester) async {
    final db = newTestDatabase();
    addTearDown(db.close);
    final connection = await offlineConn();
    await pumpSmall(
      tester,
      wrapScreen(
        repository: newTestRepository(db),
        locale: AppLocale.es,
        child: ChatScreen(
          social: await SocialService.fromRootSeedHex('00' * 32),
          connection: connection,
          peerPubkey: 'ab' * 32,
        ),
      ),
    );
    await disposeTree(tester);
  });

  testWidgets('inventory (empty) fits a small screen', (tester) async {
    final db = newTestDatabase();
    addTearDown(db.close);
    await pumpSmall(
      tester,
      wrapScreen(
        repository: newTestRepository(db),
        locale: AppLocale.es,
        child: const InventoryListScreen(),
      ),
    );
    await disposeTree(tester);
  });

  testWidgets('inventory with a long-named seed fits a small screen',
      (tester) async {
    final db = newTestDatabase();
    addTearDown(db.close);
    final repo = newTestRepository(db);
    final id = await repo.addQuickVariety(
      label: 'Tomate rosa de Barbastro extraordinariamente larguísimo',
      category: 'Solanáceas',
    );
    await repo.addLot(
      varietyId: id,
      harvestYear: 2024,
      quantity: const Quantity(kind: QuantityKind.handful),
      offerStatus: OfferStatus.shared,
    );
    await pumpSmall(
      tester,
      wrapScreen(
        repository: repo,
        locale: AppLocale.es,
        child: const InventoryListScreen(),
      ),
    );
    await disposeTree(tester);
  });

  testWidgets('the Plantares screen (with a commitment) fits a small screen',
      (tester) async {
    final db = newTestDatabase();
    addTearDown(db.close);
    final repo = newTestRepository(db);
    await repo.createPlantare(
      direction: PlantareDirection.iReturn,
      counterparty: 'Colectivo semillero de la comarca',
      owedDescription: 'un puñado la próxima temporada, si germina bien',
    );
    await pumpSmall(
      tester,
      wrapScreen(
        repository: repo,
        locale: AppLocale.es,
        child: const PlantaresScreen(),
      ),
    );
    await disposeTree(tester);
  });

  testWidgets('the Sales screen (with a sale) fits a small screen',
      (tester) async {
    final db = newTestDatabase();
    addTearDown(db.close);
    final repo = newTestRepository(db);
    await repo.createSale(
      direction: SaleDirection.iSold,
      counterparty: 'Feria de intercambio de la comarca',
      amount: 12.5,
      currency: '€',
    );
    await pumpSmall(
      tester,
      wrapScreen(
        repository: repo,
        locale: AppLocale.es,
        child: const SalesScreen(),
      ),
    );
    await disposeTree(tester);
  });

  testWidgets('the "this month" calendar fits a small screen', (tester) async {
    final db = newTestDatabase();
    addTearDown(db.close);
    final repo = newTestRepository(db);
    final id = await repo.addQuickVariety(
        label: 'Lechuga maravilla de verano', category: 'Asteráceas');
    await repo.updateVariety(id: id, sowMonths: Value(monthsToMask([3])));
    await pumpSmall(
      tester,
      wrapScreen(
        repository: repo,
        locale: AppLocale.es,
        child: const CalendarScreen(initialMonth: 3),
      ),
    );
    await disposeTree(tester);
  });

  // NOTE: the variety-detail screen is deliberately NOT overflow-checked here.
  // It reports a ~2px overflow on a RenderFlex that is already DISPOSED/DEFUNCT
  // — a transient stale frame while its cubit's Drift stream rebuilds during
  // pumpAndSettle, not the stable rendered layout. Guarding it would be flaky;
  // its RTL/build coverage lives in rtl_smoke_test.dart.
}
