using System;
using System.Collections.Generic;
using Microsoft.Data.Sqlite;
using Database.Dto;
using Team2;
using Team3;
using Team4;

namespace Tests
{
	public static class TestRunner
	{
		private static int _passed;
		private static int _failed;

		public static void RunAll()
		{
			_passed = 0;
			_failed = 0;

			Run(Test_AddBook_Success);
			Run(Test_AddBook_MissingAuthor_Fails);
			Run(Test_AddBook_MissingPublisher_Fails);
			Run(Test_AddBook_Duplicate_AllowsWithoutUniqueIndex);
			Run(Test_AddBook_EmptyName_Succeeds);
			Run(Test_AddBook_NullName_Fails);
			Run(Test_AddBook_NegativeValues_Succeeds);
			Run(Test_AddBook_AvailableGreaterThanTotal_Succeeds);
			Run(Test_Author_DuplicateSameDOB_AllowedWithoutUniqueIndex);
			Run(Test_AddAuthor_EmptyName_Succeeds_And_NullName_Fails);
			Run(Test_AddPublisher_Success_Duplicates_And_NullName_Fails);
			Run(Test_AddUser_Success_And_DuplicatesAllowed);
			Run(Test_Borrow_Success);
			Run(Test_Borrow_DueDate_Default_30Days);
			Run(Test_Borrow_MissingUser_Fails);
			Run(Test_Borrow_MissingBook_Fails);
			Run(Test_GetBooksByName_MultipleMatches);
			Run(Test_GetBooksByAuthor_ReturnsBooks);
			Run(Test_GetBooksByAvailableBooks_Filter);
			Run(Test_StolenBooks_Scenarios);
			Run(Test_StolenBooks_None_ReturnsEmpty);
			Run(Test_GetAuthorById_Success_And_NotFound);
			Run(Test_Team4_Methods_Throw_On_ClosedConnection);
			Run(Test_Team4_All_Methods_Throw_On_ClosedConnection);
			Run(Test_Team4_NotImplemented_Methods_Throw);
			Run(Test_EditAuthor_Success_And_NotFound);
			Run(Test_EditPublisher_Success_And_NotFound);
			Run(Test_EditUser_Success_And_NotFound);
			Run(Test_EditBook_Success_And_Failures);

			Console.WriteLine();
			Console.WriteLine($"Tests finished. Passed: {_passed}, Failed: {_failed}");
		}

		private static void Run(Action test)
		{
			string name = test.Method.Name;
			try
			{
				test();
				_passed++;
				Console.WriteLine($"[PASS] {name}");
			}
			catch (Exception ex)
			{
				_failed++;
				Console.WriteLine($"[FAIL] {name}: {ex.Message}");
			}
		}

		private static SqliteConnection NewMemoryConnection()
		{
			SqliteConnection conn = new SqliteConnection("Data Source=:memory:");
			conn.Open();
			Database.Database.CreateDatabaseQuery(conn);
			return conn;
		}

		private static int CountBooks(SqliteConnection conn, string name, int authorId, int publisherId, int year)
		{
			using (SqliteCommand cmd = conn.CreateCommand())
			{
				cmd.CommandText = @"SELECT COUNT(*) FROM Books 
									WHERE Name = @name AND AuthorID = @aid AND PublisherID = @pid AND YearOfRelease = @year;";
				cmd.Parameters.AddWithValue("@name", name);
				cmd.Parameters.AddWithValue("@aid", authorId);
				cmd.Parameters.AddWithValue("@pid", publisherId);
				cmd.Parameters.AddWithValue("@year", year);
				return Convert.ToInt32(cmd.ExecuteScalar());
			}
		}

		private static int CountAuthors(SqliteConnection conn, string name, string surname, DateTime dob)
		{
			using (SqliteCommand cmd = conn.CreateCommand())
			{
				cmd.CommandText = @"SELECT COUNT(*) FROM Authors 
									WHERE Name = @name AND Surname = @surname AND DateOfBirth = @dob;";
				cmd.Parameters.AddWithValue("@name", name);
				cmd.Parameters.AddWithValue("@surname", surname);
				cmd.Parameters.AddWithValue("@dob", dob.ToString("yyyy-MM-dd"));
				return Convert.ToInt32(cmd.ExecuteScalar());
			}
		}

		private static int CountUsers(SqliteConnection conn, string name, string surname)
		{
			using (SqliteCommand cmd = conn.CreateCommand())
			{
				cmd.CommandText = @"SELECT COUNT(*) FROM Users WHERE Name = @name AND Surname = @surname;";
				cmd.Parameters.AddWithValue("@name", name);
				cmd.Parameters.AddWithValue("@surname", surname);
				return Convert.ToInt32(cmd.ExecuteScalar());
			}
		}

		private static int CountBorrowsForUserBook(SqliteConnection conn, int userId, int bookId)
		{
			using (SqliteCommand cmd = conn.CreateCommand())
			{
				cmd.CommandText = @"SELECT COUNT(*) FROM Borrows WHERE UserID = @uid AND BookID = @bid;";
				cmd.Parameters.AddWithValue("@uid", userId);
				cmd.Parameters.AddWithValue("@bid", bookId);
				return Convert.ToInt32(cmd.ExecuteScalar());
			}
		}

		private static Publisher EnsurePublisher(SqliteConnection conn, string name = "Penguin", string state = "US")
		{
			Publisher p = Team2.Team2.AddPublisher(conn, name, state);
			if (p == null || p.Id <= 0) throw new Exception("Publisher creation failed");
			return p;
		}

		private static AuthorDto EnsureAuthor(SqliteConnection conn, string name = "John", string surname = "Doe", int year = 1990, int month = 1, int day = 1)
		{
			AuthorDto a = Team2.Team2.AddAuthor(conn, name, surname, new DateTime(year, month, day));
			if (a == null || a.Id <= 0) throw new Exception("Author creation failed");
			return a;
		}

		private static UserDto EnsureUser(SqliteConnection conn, string name = "Alice", string surname = "Smith")
		{
			UserDto u = Team2.Team2.AddUser(conn, name, surname);
			if (u == null || u.Id <= 0) throw new Exception("User creation failed");
			return u;
		}

		private static BookDto EnsureBook(SqliteConnection conn, string title = "Sample Book", int year = 2001, int total = 3, int available = 3)
		{
			AuthorDto author = EnsureAuthor(conn);
			Publisher publisher = EnsurePublisher(conn);
			BookDto book = Team2.Team2.AddBook(conn, title, author.Id, publisher.Id, year, total, available);
			if (book == null || book.Id <= 0) throw new Exception("Book creation failed");
			return book;
		}

		// Tests

		// Team2 - Books
		private static void Test_AddBook_Success()
		{
			using (SqliteConnection conn = NewMemoryConnection())
			{
				AuthorDto author = EnsureAuthor(conn);
				Publisher publisher = EnsurePublisher(conn);
				BookDto book = Team2.Team2.AddBook(conn, "Clean Code", author.Id, publisher.Id, 2008, 10, 10);
				if (book == null || book.Id <= 0) throw new Exception("Expected book to be created");
				if (book.Name != "Clean Code") throw new Exception("Name mismatch");
				if (book.YearOfRelease != 2008) throw new Exception("Year mismatch");
				if (book.Total != 10) throw new Exception("Total mismatch");
				int cnt = CountBooks(conn, "Clean Code", author.Id, publisher.Id, 2008);
				if (cnt != 1) throw new Exception("Expected exactly one row for the book");
			}
		}

		private static void Test_AddBook_MissingAuthor_Fails()
		{
			using (SqliteConnection conn = NewMemoryConnection())
			{
				Publisher publisher = EnsurePublisher(conn);
				BookDto book = Team2.Team2.AddBook(conn, "No Author Book", 99999, publisher.Id, 2020, 1, 1);
				if (book != null) throw new Exception("Expected null when author does not exist");
			}
		}

		private static void Test_AddBook_MissingPublisher_Fails()
		{
			using (SqliteConnection conn = NewMemoryConnection())
			{
				AuthorDto author = EnsureAuthor(conn);
				BookDto book = Team2.Team2.AddBook(conn, "No Publisher Book", author.Id, 99999, 2020, 1, 1);
				if (book != null) throw new Exception("Expected null when publisher does not exist");
			}
		}

		private static void Test_AddBook_Duplicate_AllowsWithoutUniqueIndex()
		{
			using (SqliteConnection conn = NewMemoryConnection())
			{
				AuthorDto author = EnsureAuthor(conn);
				Publisher publisher = EnsurePublisher(conn);
				BookDto b1 = Team2.Team2.AddBook(conn, "Duplicate", author.Id, publisher.Id, 2021, 2, 2);
				if (b1 == null) throw new Exception("First insert failed");
				BookDto b2 = Team2.Team2.AddBook(conn, "Duplicate", author.Id, publisher.Id, 2021, 2, 2);
				if (b2 == null) throw new Exception("Second insert unexpectedly failed");
				int cnt = CountBooks(conn, "Duplicate", author.Id, publisher.Id, 2021);
				if (cnt < 2) throw new Exception("Expected duplicates without a unique index");
			}
		}

		private static void Test_AddBook_EmptyName_Succeeds()
		{
			using (SqliteConnection conn = NewMemoryConnection())
			{
				AuthorDto author = EnsureAuthor(conn);
				Publisher publisher = EnsurePublisher(conn);
				BookDto book = Team2.Team2.AddBook(conn, "", author.Id, publisher.Id, 2000, 1, 1);
				if (book == null) throw new Exception("Empty string name should be allowed (NOT NULL != empty)");
			}
		}

		private static void Test_AddBook_NullName_Fails()
		{
			using (SqliteConnection conn = NewMemoryConnection())
			{
				AuthorDto author = EnsureAuthor(conn);
				Publisher publisher = EnsurePublisher(conn);
				#pragma warning disable CS8625, CS8604, CS8600
				BookDto book = Team2.Team2.AddBook(conn, (string)(object)null, author.Id, publisher.Id, 2000, 1, 1);
				#pragma warning restore CS8625, CS8604, CS8600
				if (book != null) throw new Exception("Expected null when inserting NULL into NOT NULL column");
			}
		}

		private static void Test_AddBook_NegativeValues_Succeeds()
		{
			using (SqliteConnection conn = NewMemoryConnection())
			{
				AuthorDto author = EnsureAuthor(conn);
				Publisher publisher = EnsurePublisher(conn);
				BookDto book = Team2.Team2.AddBook(conn, "NegativeVals", author.Id, publisher.Id, -1, -5, -2);
				if (book == null) throw new Exception("Negative values should not be validated at DB level here");
				if (book.YearOfRelease != -1) throw new Exception("Year not stored as provided");
				if (book.Total != -5) throw new Exception("Total not stored as provided");
			}
		}

		private static void Test_AddBook_AvailableGreaterThanTotal_Succeeds()
		{
			using (SqliteConnection conn = NewMemoryConnection())
			{
				AuthorDto author = EnsureAuthor(conn);
				Publisher publisher = EnsurePublisher(conn);
				BookDto book = Team2.Team2.AddBook(conn, "AvailGtTotal", author.Id, publisher.Id, 2010, 1, 5);
				if (book == null) throw new Exception("Available > Total currently not validated; insert should succeed");
			}
		}

		private static void Test_Author_DuplicateSameDOB_AllowedWithoutUniqueIndex()
		{
			using (SqliteConnection conn = NewMemoryConnection())
			{
				string name = "Jane";
				string surname = "Doe";
				DateTime dob = new DateTime(1995, 5, 5);
				AuthorDto a1 = Team2.Team2.AddAuthor(conn, name, surname, dob);
				if (a1 == null) throw new Exception("First author insert failed");
				AuthorDto a2 = Team2.Team2.AddAuthor(conn, name, surname, dob);
				if (a2 == null) throw new Exception("Second author insert unexpectedly failed");
				int cnt = CountAuthors(conn, name, surname, dob);
				if (cnt < 2) throw new Exception("Expected duplicate authors without a unique index");
			}
		}

		// Team2 - Users
		private static void Test_AddAuthor_EmptyName_Succeeds_And_NullName_Fails()
		{
			using (SqliteConnection conn = NewMemoryConnection())
			{
				// Empty name should be allowed (NOT NULL != empty)
				AuthorDto a1 = Team2.Team2.AddAuthor(conn, "", "S", new DateTime(1990, 1, 1));
				if (a1 == null || a1.Id <= 0) throw new Exception("Empty author name should be allowed");
				// Null name should fail and return null
				#pragma warning disable CS8625, CS8604, CS8600
				AuthorDto a2 = Team2.Team2.AddAuthor(conn, (string)(object)null, "S", new DateTime(1990, 1, 1));
				#pragma warning restore CS8625, CS8604, CS8600
				if (a2 != null) throw new Exception("Expected null when inserting NULL author name");
			}
		}

		private static void Test_AddPublisher_Success_Duplicates_And_NullName_Fails()
		{
			using (SqliteConnection conn = NewMemoryConnection())
			{
				Publisher p1 = Team2.Team2.AddPublisher(conn, "PubX", "CZ");
				if (p1 == null || p1.Id <= 0) throw new Exception("Publisher insert failed");
				Publisher p2 = Team2.Team2.AddPublisher(conn, "PubX", "CZ");
				if (p2 == null || p2.Id <= 0) throw new Exception("Duplicate publisher insert unexpectedly failed");
				#pragma warning disable CS8625, CS8604, CS8600
				Publisher p3 = Team2.Team2.AddPublisher(conn, (string)(object)null, "CZ");
				#pragma warning restore CS8625, CS8604, CS8600
				if (p3 != null) throw new Exception("Expected null when inserting NULL publisher name");
			}
		}

		private static void Test_AddUser_Success_And_DuplicatesAllowed()
		{
			using (SqliteConnection conn = NewMemoryConnection())
			{
				UserDto u1 = Team2.Team2.AddUser(conn, "Bob", "Brown");
				if (u1 == null || u1.Id <= 0) throw new Exception("User insert failed");
				UserDto u2 = Team2.Team2.AddUser(conn, "Bob", "Brown");
				if (u2 == null || u2.Id <= 0) throw new Exception("Duplicate user insert unexpectedly failed");
				int cnt = CountUsers(conn, "Bob", "Brown");
				if (cnt < 2) throw new Exception("Expected duplicate users without a unique index");
			}
		}

		// Team2 - Borrow
		private static void Test_Borrow_Success()
		{
			using (SqliteConnection conn = NewMemoryConnection())
			{
				UserDto user = EnsureUser(conn);
				BookDto book = EnsureBook(conn, "Borrowed Book", 2015, 5, 5);
				DateTime borrowed = DateTime.UtcNow.Date;
				Borrow borrow = Team2.Team2.AddBorrowedBookRecord(conn, book.Id, user.Id, borrowed);
				if (borrow == null || borrow.Id <= 0) throw new Exception("Borrow creation failed");
				if (borrow.User == null || borrow.User.Id != user.Id) throw new Exception("Borrow.User mismatch");
				if (borrow.Book == null || borrow.Book.Id != book.Id) throw new Exception("Borrow.Book mismatch");
				int cnt = CountBorrowsForUserBook(conn, user.Id, book.Id);
				if (cnt != 1) throw new Exception("Expected exactly one borrow record");
			}
		}

		private static void Test_Borrow_DueDate_Default_30Days()
		{
			using (SqliteConnection conn = NewMemoryConnection())
			{
				UserDto user = EnsureUser(conn);
				BookDto book = EnsureBook(conn, "Borrowed Book 2", 2016, 2, 2);
				DateTime borrowed = new DateTime(2020, 1, 1);
				Borrow borrow = Team2.Team2.AddBorrowedBookRecord(conn, book.Id, user.Id, borrowed);
				if (borrow == null) throw new Exception("Borrow creation failed");
				DateTime expectedDue = borrowed.AddDays(30);
				if (borrow.ReturnDue.Date != expectedDue.Date) throw new Exception($"ReturnDue mismatch: got {borrow.ReturnDue.Date:yyyy-MM-dd}, expected {expectedDue:yyyy-MM-dd}");
			}
		}

		private static void Test_Borrow_MissingUser_Fails()
		{
			using (SqliteConnection conn = NewMemoryConnection())
			{
				BookDto book = EnsureBook(conn);
				Borrow borrow = Team2.Team2.AddBorrowedBookRecord(conn, book.Id, 999999, DateTime.UtcNow.Date);
				if (borrow != null) throw new Exception("Expected null when borrowing with missing user");
			}
		}

		private static void Test_Borrow_MissingBook_Fails()
		{
			using (SqliteConnection conn = NewMemoryConnection())
			{
				UserDto user = EnsureUser(conn);
				Borrow borrow = Team2.Team2.AddBorrowedBookRecord(conn, 999999, user.Id, DateTime.UtcNow.Date);
				if (borrow != null) throw new Exception("Expected null when borrowing with missing book");
			}
		}

		// Team4 - Queries and exceptions
		private static void Test_GetBooksByName_MultipleMatches()
		{
			using (SqliteConnection conn = NewMemoryConnection())
			{
				BookDto b1 = EnsureBook(conn, "SameName", 2000, 1, 1);
				BookDto b2 = EnsureBook(conn, "SameName", 2001, 2, 2);
				List<BookDto> books = Team4.Team4.GetBooksByName(conn, "SameName");
				if (books == null || books.Count < 2) throw new Exception("Expected at least two books with same name");
			}
		}

		private static void Test_GetBooksByAuthor_ReturnsBooks()
		{
			using (SqliteConnection conn = NewMemoryConnection())
			{
				AuthorDto author = EnsureAuthor(conn, "Auth", "One", 1980, 1, 1);
				Publisher publisher = EnsurePublisher(conn, "P1", "US");
				BookDto b1 = Team2.Team2.AddBook(conn, "ByAuthor1", author.Id, publisher.Id, 2010, 1, 1);
				BookDto b2 = Team2.Team2.AddBook(conn, "ByAuthor2", author.Id, publisher.Id, 2011, 1, 1);
				List<BookDto> books = Team4.Team4.GetBooksByAuthor(conn, author);
				if (books == null || books.Count < 2) throw new Exception("Expected two books by author");
			}
		}

		private static void Test_GetBooksByAvailableBooks_Filter()
		{
			using (SqliteConnection conn = NewMemoryConnection())
			{
				BookDto b1 = EnsureBook(conn, "Stock1", 2000, 0, 0);
				BookDto b2 = EnsureBook(conn, "Stock2", 2001, 2, 2);
				List<BookDto> books = Team4.Team4.GetBooksByAvailableBooks(conn, 1);
				if (books == null) throw new Exception("Expected list, got null");
				bool hasStock2 = false;
				for (int i = 0; i < books.Count; i++)
				{
					if (books[i].Name == "Stock2") { hasStock2 = true; break; }
				}
				if (!hasStock2) throw new Exception("Expected Stock2 in results for minStock=1");
			}
		}

		private static void Test_StolenBooks_Scenarios()
		{
			using (SqliteConnection conn = NewMemoryConnection())
			{
				UserDto user = EnsureUser(conn);
				BookDto book = EnsureBook(conn, "Overdue", 2005, 1, 1);
				// Borrow far in the past so ReturnDue < now
				DateTime borrowedPast = DateTime.UtcNow.Date.AddDays(-60);
				Borrow br = Team2.Team2.AddBorrowedBookRecord(conn, book.Id, user.Id, borrowedPast);
				if (br == null) throw new Exception("Failed to create overdue borrow");
				List<BookDto> stolenAll = Team4.Team4.GetStolenBooks(conn);
				if (stolenAll == null || stolenAll.Count == 0) throw new Exception("Expected stolen books to include the overdue borrow");
				List<BookDto> stolenByUser = Team4.Team4.GetStolenBooksByUser(conn, user);
				if (stolenByUser == null || stolenByUser.Count == 0) throw new Exception("Expected stolen books by user");
			}
		}

		private static void Test_StolenBooks_None_ReturnsEmpty()
		{
			using (SqliteConnection conn = NewMemoryConnection())
			{
				List<BookDto> stolenAll = Team4.Team4.GetStolenBooks(conn);
				if (stolenAll == null) throw new Exception("Expected empty list, got null");
				if (stolenAll.Count != 0) throw new Exception("Expected no stolen books initially");
			}
		}

		private static void Test_GetAuthorById_Success_And_NotFound()
		{
			using (SqliteConnection conn = NewMemoryConnection())
			{
				AuthorDto a = EnsureAuthor(conn, "Lookup", "Author", 1970, 1, 1);
				AuthorDto ok = Team4.Team4.GetAuthorById(conn, a.Id);
				if (ok == null || ok.Id != a.Id) throw new Exception("Expected author lookup success");
				AuthorDto missing = Team4.Team4.GetAuthorById(conn, 999999);
				if (missing != null) throw new Exception("Expected null for missing author");
			}
		}

		private static void Test_Team4_Methods_Throw_On_ClosedConnection()
		{
			SqliteConnection conn = new SqliteConnection("Data Source=:memory:");
			conn.Open();
			Database.Database.CreateDatabaseQuery(conn);
			conn.Close();
			bool threw = false;
			try
			{
				List<BookDto> _ = Team4.Team4.GetBooksByName(conn, "Anything");
			}
			catch (Exception)
			{
				threw = true;
			}
			if (!threw) throw new Exception("Expected Team4.GetBooksByName to throw on closed connection");
		}

		private static void Test_Team4_All_Methods_Throw_On_ClosedConnection()
		{
			SqliteConnection conn = new SqliteConnection("Data Source=:memory:");
			conn.Open();
			Database.Database.CreateDatabaseQuery(conn);
			conn.Close();
			string fail(string method) { return $"Expected {method} to throw on closed connection"; }
			try { Team4.Team4.GetBooksByAuthor(conn, new AuthorDto { Id = 1, Name = "", Surname = "", DateOfBirth = new DateTime(2000, 1, 1) }); throw new Exception(fail("GetBooksByAuthor")); } catch (Exception) { }
			try { Team4.Team4.GetBooksByAvailableBooks(conn, 1); throw new Exception(fail("GetBooksByAvailableBooks")); } catch (Exception) { }
			try { Team4.Team4.GetStolenBooks(conn); throw new Exception(fail("GetStolenBooks")); } catch (Exception) { }
			try { Team4.Team4.GetStolenBooksByUser(conn, new UserDto { Id = 1, Name = "", Surname = "" }); throw new Exception(fail("GetStolenBooksByUser")); } catch (Exception) { }
			try { Team4.Team4.GetAuthorById(conn, 1); throw new Exception(fail("GetAuthorById")); } catch (Exception) { }
		}

		private static void Test_Team4_NotImplemented_Methods_Throw()
		{
			using (SqliteConnection conn = NewMemoryConnection())
			{
				bool threw1 = false;
				try { Team4.Team4.GetBooksBy(conn); } catch (NotImplementedException) { threw1 = true; }
				if (!threw1) throw new Exception("Expected NotImplementedException from GetBooksBy");
				bool threw2 = false;
				try { Team4.Team4.GetBorrowLogBy(conn); } catch (NotImplementedException) { threw2 = true; }
				if (!threw2) throw new Exception("Expected NotImplementedException from GetBorrowLogBy");
			}
		}

		// Team3 - Edit operations
		private static void Test_EditAuthor_Success_And_NotFound()
		{
			using (SqliteConnection conn = NewMemoryConnection())
			{
				AuthorDto a = EnsureAuthor(conn, "Old", "Name", 1985, 2, 2);
				a.Name = "New";
				a.Surname = "Surname";
				AuthorDto? updated = EditRecords.EditAuthor(conn, a);
				if (updated == null || updated.Name != "New" || updated.Surname != "Surname") throw new Exception("EditAuthor failed to update");
				AuthorDto? notFound = EditRecords.EditAuthor(conn, new AuthorDto { Id = 999999, Name = "X", Surname = "Y", DateOfBirth = new DateTime(1990, 1, 1) });
				if (notFound != null) throw new Exception("Expected null for non-existing author");
			}
		}

		private static void Test_EditPublisher_Success_And_NotFound()
		{
			using (SqliteConnection conn = NewMemoryConnection())
			{
				Publisher p = EnsurePublisher(conn, "OldPub", "UK");
				p.Name = "NewPub";
				p.State = "CZ";
				Publisher? updated = EditRecords.EditPublisher(conn, p);
				if (updated == null || updated.Name != "NewPub" || updated.State != "CZ") throw new Exception("EditPublisher failed to update");
				Publisher? notFound = EditRecords.EditPublisher(conn, new Publisher { Id = 999999, Name = "X", State = "Y" });
				if (notFound != null) throw new Exception("Expected null for non-existing publisher");
			}
		}

		private static void Test_EditUser_Success_And_NotFound()
		{
			using (SqliteConnection conn = NewMemoryConnection())
			{
				UserDto u = EnsureUser(conn, "Carl", "Jr");
				u.Name = "Carlos";
				u.Surname = "Senior";
				UserDto? updated = EditRecords.EditUser(conn, u);
				if (updated == null || updated.Name != "Carlos" || updated.Surname != "Senior") throw new Exception("EditUser failed to update");
				UserDto? notFound = EditRecords.EditUser(conn, new UserDto { Id = 999999, Name = "X", Surname = "Y" });
				if (notFound != null) throw new Exception("Expected null for non-existing user");
			}
		}

		private static void Test_EditBook_Success_And_Failures()
		{
			using (SqliteConnection conn = NewMemoryConnection())
			{
				// Seed original
				AuthorDto a1 = EnsureAuthor(conn, "A", "One", 1970, 1, 1);
				Publisher p1 = EnsurePublisher(conn, "Pub1", "US");
				BookDto b = Team2.Team2.AddBook(conn, "Original", a1.Id, p1.Id, 1999, 1, 1);
				if (b == null) throw new Exception("Seeding book failed");
				// Prepare updates
				AuthorDto a2 = EnsureAuthor(conn, "A", "Two", 1975, 1, 1);
				Publisher p2 = EnsurePublisher(conn, "Pub2", "DE");
				b.Name = "Updated";
				b.Author = a2;
				b.Publisher = p2;
				b.YearOfRelease = 2000;
				b.Total = 5;
				BookDto? updated = EditRecords.EditBook(conn, b);
				if (updated == null || updated.Name != "Updated" || updated.Author.Id != a2.Id || updated.Publisher.Id != p2.Id) throw new Exception("EditBook failed to update");
				// Not found
				BookDto? notFound = EditRecords.EditBook(conn, new BookDto { Id = 999999, Name = "X", YearOfRelease = 2000, Total = 1, Author = a2, Publisher = p2 });
				if (notFound != null) throw new Exception("Expected null for non-existing book");
				// Invalid author
				BookDto invalidAuthor = new BookDto { Id = b.Id, Name = "X", YearOfRelease = 2001, Total = 1, Author = new AuthorDto { Id = 999999, Name = "Na", Surname = "Na", DateOfBirth = new DateTime(1970, 1, 1) }, Publisher = p2 };
				if (EditRecords.EditBook(conn, invalidAuthor) != null) throw new Exception("Expected null when editing with non-existing author");
				// Invalid publisher
				BookDto invalidPublisher = new BookDto { Id = b.Id, Name = "X", YearOfRelease = 2001, Total = 1, Author = a2, Publisher = new Publisher { Id = 999999, Name = "Na", State = "Na" } };
				if (EditRecords.EditBook(conn, invalidPublisher) != null) throw new Exception("Expected null when editing with non-existing publisher");
			}
		}
	}
}

